【发布时间】:2018-11-11 23:42:45
【问题描述】:
我最近看到了使用 dyn 关键字的代码:
fn foo(arg: &dyn Display) {}
fn bar() -> Box<dyn Display> {}
这个语法是什么意思?
【问题讨论】:
我最近看到了使用 dyn 关键字的代码:
fn foo(arg: &dyn Display) {}
fn bar() -> Box<dyn Display> {}
这个语法是什么意思?
【问题讨论】:
TL;DR:这是一种用于指定 trait 对象的类型的语法,为了清楚起见必须指定。
自 Rust 1.0 以来,特质过着双重生活。一旦声明了一个 trait,它就可以用作一个 trait 或一个类型:
// As a trait
impl MyTrait for SomeType {}
// As a type!
impl MyTrait {}
impl AnotherTrait for MyTrait {}
您可以想象,这种双重含义可能会引起一些混乱。此外,由于MyTrait 类型是未调整大小/动态调整大小的类型,这可能会使人们看到非常复杂的错误消息。
为了改善这个问题,RFC 2113 引入了dyn 语法。从 Rust 1.27 开始可以使用此语法:
use std::{fmt::Display, sync::Arc};
fn main() {
let display_ref: &dyn Display = &42;
let display_box: Box<dyn Display> = Box::new(42);
let display_arc: Arc<dyn Display> = Arc::new(42);
}
这个新关键字与 impl Trait 语法相似,并努力使 trait object 的类型更明显地区别于“裸”特征语法。
dyn 是“动态”的缩写,指的是 trait 对象执行 dynamic dispatch 的事实。这意味着确切调用哪个函数的决定将在程序运行时发生。将此与使用 impl Trait 语法的 static dispatch 进行对比。
不推荐使用不带dyn 的语法,并且很可能在随后的edition of Rust 中将其删除。
【讨论】:
dyn 关键字用于表示一个类型是一个 trait 对象。根据the Rust docs:
特征对象是另一种类型的不透明值,它实现了 一组特征。
也就是说,我们在编译时并不知道对象的具体类型,只知道对象实现了trait。
因为 trait 对象的大小在编译时是未知的,所以它们必须放在指针后面。例如,如果 Trait 是您的 trait 名称,那么您可以通过以下方式使用您的 trait 对象:
Box<dyn Trait>&dyn Trait保存特征对象的变量/参数是胖指针,由以下组件组成:
有关详细信息,请参阅我在 What makes something a “trait object”? 上的回答。
【讨论】:
“dyn”基本上告诉编译器我们不知道在编译时类型将是什么,而它将在运行时确定。
这是因为编译器会在第一次调用使用泛型的函数时硬编码类型来代替我们的泛型类型。对同一函数的所有其他调用都应使用与第一次调用相同的类型。
如果您想每次使用不同的类型多次调用函数,则必须将泛型类型标记为“dyn”,这将导致该泛型类型在每次调用时在运行时被解析.
警告 与所有事情一样,实现额外的灵活性会降低性能,而这种情况肯定会降低性能。
我发现这篇博文非常清楚地解释了这个功能:https://medium.com/digitalfrontiers/rust-dynamic-dispatching-deep-dive-236a5896e49b
相关摘录:
struct Service<T:Backend>{
backend: Vec<T> // Either Vec<TypeA> or Vec<TypeB>, not both
}
...
let mut backends = Vec::new();
backends.push(TypeA);
backends.push(TypeB); // <---- Type error here
对
struct Service{
backends: Vec<Box<dyn Backend>>
}
...
let mut backends = Vec::new();
backends.push( Box::new(PositiveBackend{}) as Box<dyn Backend>);
backends.push( Box::new(NegativeBackend{}) as Box<dyn Backend>);
【讨论】: