Trait 对象
语法
TraitObjectType :
dyn? 类型参数约束TraitObjectTypeOneBound :
dyn? Trait 约束
一个trait 对象是另一种类型的非透明值,它实现了一组 trait。这组 trait 由一个dyn compatible的基础 trait以及任意数量的自动 trait组成。
Trait 对象实现基础 trait、其自动 trait 以及基础 trait 的任何超 trait。
Trait 对象以关键字dyn开头,后跟一组 trait 约束,但对 trait 约束有以下限制。
不允许有多个非自动 trait,不允许有多个生命周期,且不允许使用 opt-out 约束(例如 ?Sized)。此外,trait 的路径可以放在括号中。
例如,给定 trait Trait,以下都是 trait 对象
dyn Traitdyn Trait + Senddyn Trait + Send + Syncdyn Trait + 'staticdyn Trait + Send + 'staticdyn Trait +dyn 'static + Trait.dyn (Trait)
版本差异:在 2021 版本之前,可以省略关键字
dyn。
版本差异:在 2015 版本中,如果 trait 对象的第一个约束是以
::开头的路径,那么dyn将被视为路径的一部分。可以通过将第一个路径放在括号中来避免这种情况。因此,如果你想要一个使用 trait::your_module::Trait的 trait 对象,你应该将其写为dyn (::your_module::Trait)。从 2018 版本开始,
dyn是一个真正的关键字,不允许出现在路径中,因此不再需要括号。
如果基础 trait 互为别名,且自动 trait 的集合相同,并且生命周期约束相同,那么两种 trait 对象类型互为别名。例如,dyn Trait + Send + UnwindSafe 与 dyn Trait + UnwindSafe + Send 相同。
由于其值具体是什么类型是非透明的,因此 trait 对象是动态大小类型。与所有DSTs一样,trait 对象通过某种类型的指针使用;例如 &dyn SomeTrait 或 Box<dyn SomeTrait>。指向 trait 对象的指针的每个实例包括
- 指向实现
SomeTrait的类型T实例的指针 - 一个虚方法表,通常简称为 vtable,其中包含对于
SomeTrait及其超 trait 中由T实现的每个方法,一个指向T实现(即函数指针)的指针。
trait 对象的作用是允许方法的“晚绑定”。在 trait 对象上调用方法会导致运行时的虚派发:也就是说,从 trait 对象的 vtable 中加载一个函数指针并间接调用。每个 vtable 条目的实际实现可以因对象而异。
trait 对象示例
trait Printable { fn stringify(&self) -> String; } impl Printable for i32 { fn stringify(&self) -> String { self.to_string() } } fn print(a: Box<dyn Printable>) { println!("{}", a.stringify()); } fn main() { print(Box::new(10) as Box<dyn Printable>); }
在此示例中,trait Printable 作为 trait 对象出现在 print 的类型签名和 main 的强制转换表达式中。
Trait 对象生命周期约束
由于 trait 对象可以包含引用,因此这些引用的生命周期需要作为 trait 对象的一部分来表达。此生命周期写为 Trait + 'a。存在一些默认规则,允许通常以合理的选择来推断此生命周期。