"dyn"在一个类型中意味着什么?

She*_*ter 41 syntax rust

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

fn foo(arg: &dyn Display) {}

fn bar() -> Box<dyn Display> {}
Run Code Online (Sandbox Code Playgroud)

这个语法是什么意思?

She*_*ter 31

TL; DR:它是用于指定特征对象类型的语法,出于清晰的原因应该是首选的.


自Rust 1.0以来,特质已经过了双重生活.一旦声明了特征,它就可以用作特征或类型:

// As a trait
impl MyTrait for SomeType {}

// As a type!
impl MyTrait {}
impl AnotherTrait for MyTrait {}
Run Code Online (Sandbox Code Playgroud)

可以想象,这种双重含义会引起一些混乱.此外,由于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);
}
Run Code Online (Sandbox Code Playgroud)

这个新关键字与impl Trait语法相似,并努力使特征对象的类型与"裸"特征语法明显不同.

在随后的Rust版本中,可能会弃用裸语法,然后最终删除.


wat*_*atr 15

TLDR:“dyn”允许您在 Box 中存储苹果和橙子的混合,因为它们都实现了 Fruit 的相同特征,这就是您的 Box 用作类型约束的特征,而不仅仅是通用类型。这是因为 Generic 允许使用 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
Run Code Online (Sandbox Code Playgroud)

如果要将多种类型存储到数据结构的同一实例,则必须使用包装泛型类型的特征并将其标记为“dyn”,这将导致每次调用该泛型类型时都会解析它,在运行时。

有时,我们不使用类型(String、&str、i32 等)或泛型(T、Vec 等),而是使用特征作为类型约束(即 TryFrom)。这是为了允许我们在同一个数据结构实例中存储多种类型(所有类型都实现所需的特征)(您可能也需要 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
Run Code Online (Sandbox Code Playgroud)

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>);
Run Code Online (Sandbox Code Playgroud)


Wil*_*een 5

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

\n
\n

特征对象是实现一组特征的另一种类型的不透明值。\n

\n
\n

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

\n

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

\n
    \n
  • Box<dyn Trait>
  • \n
  • &dyn Trait
  • \n
  • 和其他指针类型
  • \n
\n

保存特征对象的变量/参数是胖指针,由以下组件组成:

\n
    \n
  • 指向内存中对象的指针
  • \n
  • 指向该对象\xe2\x80\x99s vtable 的指针,vtable 是一个带有指向实际方法实现的指针的表。
  • \n
\n

请参阅我的回答“什么使某物成为 \xe2\x80\x9ctrait 对象\xe2\x80\x9d?” 了解更多详情。

\n