Trait 对象
语法
Trait对象类型 :
dyn
? [类型参数约束组]Trait对象类型单约束 :
dyn
? Trait约束
trait 对象 是另一个类型的不透明值,该类型实现了一组 trait 。trait 集由一个 对象安全 的 base trait 和任意数量的 [auto trait] 组成。
Trait 对象实现了 base trait ,它的 auto traits 以及 base trait 的任何 父级trait 。
Trait 对象写作关键字 dyn
后跟一组 trait 约束 ,但对 trait 约束有以下限制。
除了第一个 trait 之外的所有 trait 都必须是 auto trait ,不能有多个生命周期,并且不允许可选附加约束 (例如 ?Sized
)。
此外,路径到 traits 可以括在括号中。
例如,给定 Trait
,以下都是 trait 对象:
dyn Trait
dyn Trait + Send
dyn Trait + Send + Sync
dyn Trait + 'static
dyn Trait + Send + 'static
dyn Trait +
dyn 'static + Trait
.dyn (Trait)
版本差异: 在 2021 版之前,可以省略
dyn
关键字。注意: 为了清晰起见,建议在你的 trait 对象上始终使用
dyn
关键字,除非你的代码库支持使用 Rust 1.26 或更低版本进行编译。
版本差异: 在 2015 版中,如果 trait 对象的第一个限定是以
::
开头的路径,那么dyn
将被视为路径的一部分。你可以将第一个路径放在括号中来解决这个问题。 因此,如果你想要一个带有 trait::your_module::Trait
的 trait 对象,你应该将其写为dyn (::your_module::Trait)
。从 2018 版开始,
dyn
是正式的关键字,不允许在路径中使用,因此不需要括号。
如果 base trait 相互为别名, auto trait 集相同且生命周期约束相同,则两个 trait 对象类型为别名。例如, dyn Trait + Send + UnwindSafe
与 dyn Trait + UnwindSafe + Send
是相同的。
由于值的具体类型的不透明性,trait 对象是 动态大小类型 。与所有 DSTs 一样,trait 对象在某种类型的指针后面使用,例如 &dyn SomeTrait
或 Box<dyn SomeTrait>
。
指向 trait 对象的指针实例包括:
- 指向实现
SomeTrait
的类型T
的实例的指针 - 虚方法表 (通常简称为 vtable ) ,它包含对于
T
实现的每个方法及其 父级trait ,指向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
出现在 print
函数的类型签名和 main
函数中的转换表达式中,表示它们都是 trait 对象。
Trait 对象的生命周期约束
由于 Trait 对象可以包含引用,因此这些引用的生命周期需要作为 Trait 对象的一部分表示。这个生命周期的写法是 Trait + 'a
。有一些 默认值 可以允许此生命周期通常被推断为一个合理的选择。