“dyn”在类型中是什么意思?

107

我最近看到了使用 dyn 关键字的代码:

fn foo(arg: &dyn Display) {}

fn bar() -> Box<dyn Display> {}

这个语法是什么意思?

3个回答

94
TL;DR:这是一种用于指定特质对象类型的语法,出于清晰度的考虑必须进行明确说明。
自从Rust 1.0版本以来,特质一直过着双重生活。一旦声明了一个特质,它可以被用作特质或类型的形式使用。
// 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的类型与“裸”trait语法更明显地区分开来。
`dyn`是“dynamic”的缩写,指的是trait对象执行动态分派的事实。这意味着在程序运行时将决定调用哪个函数。与使用`impl Trait`语法的静态分派形成对比。
不带有`dyn`的语法现已被弃用,并且在2021版中已被移除

14

简介:"dyn" 允许您将苹果和橙子混合存储在一个 Box 中,因为它们都实现了 Fruit 这个 trait,而这正是您的 Box 用作类型约束的内容,而不仅仅是一个通用类型。这是因为泛型只允许其中的一个是 Apple 或 Orange,而不是两者都是:

Vec<Box<T>> --> Vector can hold boxes of either Apples OR Oranges structs
Vec<Box<dyn Fruit>> --> Vector can now hold a mix of boxes of Apples AND Oranges Structs

如果您想要将多种类型存储到同一数据结构实例中,您必须使用封装泛型类型的特质,并将其标记为“dyn”,这将导致每次调用时解析该泛型类型,在运行时进行调用。
有时候,我们使用特质作为类型限制(例如TryFrom),而不是使用具体类型(如String、&str、i32等)或泛型(例如T、Vec等)。这是为了让我们可以在同一数据结构实例中存储多个类型(都实现所需的特质),您可能需要对其进行Box<>处理。
使用“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

vs

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>);

5
dyn关键字用于指示类型为特征对象。根据Rust文档

特征对象是另一种类型的不透明值,它实现了一组特征。

换句话说,我们在编译时不知道对象的具体类型,只知道该对象实现了该特征。
由于特征对象的大小在编译时是未知的,因此必须将其放在指针后面。例如,如果Trait是您的特征名称,则可以按以下方式使用特征对象:
  • Box<dyn Trait>
  • &dyn Trait
  • 和其他指针类型
保存特征对象的变量/参数是包含以下组件的fat pointers:
  • 指向内存中对象的指针
  • 指向该对象的vtable的指针,vtable是一个带有指向实际方法实现的指针的表。

请查看我的什么是“trait对象”的回答以获取更多细节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接