代码之家  ›  专栏  ›  技术社区  ›  Shepmaster Tim Diekmann

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

  •  87
  • Shepmaster Tim Diekmann  · 技术社区  · 6 年前

    我最近看到了使用 dyn 关键词:

    fn foo(arg: &dyn Display) {}
    
    fn bar() -> Box<dyn Display> {}
    

    这个语法是什么意思?

    3 回复  |  直到 6 年前
        1
  •  79
  •   Stargateur    4 年前

    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 与“裸”特征语法更明显地不同。

    戴恩 是“动态”的缩写,指特质对象执行 dynamic dispatch 这意味着在程序运行时将决定调用哪个函数。将此与 static dispatch 使用 简单特征 语法。

    语法没有 戴恩 现在已经不推荐了,很可能在以后的 edition of Rust 它将被移除。

        2
  •  3
  •   Shepmaster Tim Diekmann    4 年前

    这个 dyn 关键字用于指示类型是特征对象。根据 the Rust docs :

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

    换句话说,我们在编译时不知道对象的具体类型,我们只知道对象实现了特征。

    因为trait对象的大小在编译时是未知的,所以它们必须放在指针后面。例如,如果 Trait 是你的特质名称,那么你可以用以下方式使用你的特质对象:

    • Box<dyn Trait>
    • &dyn Trait
    • 以及其他指针类型

    包含trait对象的变量/参数是fat指针,由以下组件组成:

    • 指向内存中对象的指针
    • 指向对象vtable的指针,vtable是一个表,表中的指针指向实际的方法实现。

    看到我的答案了吗 What makes something a “trait object”? 了解更多细节。

        3
  •  2
  •   watr    3 年前

    “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
    

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