Trait 对象
Syntax
TraitObjectType → dyn? Bounds
Trait 对象是实现一组 trait 的另一个类型的不透明值。该组 trait 由 dyn 兼容的基础 trait 加上任意数量的自动 trait组成。
Trait 对象实现基础 trait、其自动 trait 以及基础 trait 的任何超级 trait。
Trait 对象写为关键字 dyn 后跟一组 trait 约束,但对 trait 约束有以下限制。
不能有多个非自动 trait,不能有多个生命周期,并且不允许选择退出约束(例如 ?Sized)。此外,到 trait 的路径可以用括号括起来。
例如,给定一个 trait Trait,以下都是 trait 对象:
dyn Traitdyn Trait + Senddyn Trait + Send + Syncdyn Trait + 'staticdyn Trait + Send + 'staticdyn Trait +dyn 'static + Trait.dyn (Trait)
2021 Edition differences
在 2021 版本之前,可以省略
dyn关键字。
2018 Edition differences
在 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 对象是动态大小类型。与所有 DST 一样,trait 对象在某种指针类型后面使用;例如 &dyn SomeTrait 或 Box<dyn SomeTrait>。指向 trait 对象的指针的每个实例包括:
- 指向实现
SomeTrait的类型T的实例的指针 - 虚拟方法表,通常简称为 vtable,其中包含
T实现的SomeTrait及其超级 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。有默认值允许此生命周期通常可以通过合理的选择推断。