16-Trait 对象类型
2 分钟阅读
译文 · 基于 The Rust Reference
原文链接: https://doc.rust-lang.org/reference/types/trait-object.html
r[type.trait-object]
Trait 对象类型
r[type.trait-object.syntax]
TraitObjectType -> Bounds[^bare-2021] | `dyn`[^dyn-2018] Bounds?
TraitObjectTypeOneBound -> TraitBound[^bare-2021] | `dyn`[^dyn-2018] TraitBound?
r[type.trait-object.intro] trait 对象 是实现了一组 trait 的另一种类型的不透明值。这组 trait 由一个 dyn 兼容的 基础 trait 加上任意数量的自动 trait组成。
r[type.trait-object.impls] Trait 对象实现基础 trait、其自动 trait,以及基础 trait 的任何超 trait。
r[type.trait-object.bounds]
必须至少有一个 trait 约束,不能有多于一个非自动 trait,不能有多于一个生命周期,并且不允许退出约束(例如 ?Sized)和 use<..> 约束。
例如,给定 trait Trait,以下都是 trait 对象:
dyn Traitdyn Trait + Senddyn Trait + Send + Syncdyn Trait + 'staticdyn Trait + Send + 'staticdyn Trait +dyn 'static + Trait.dyn (Trait)
r[type.trait-object.syntax-edition2021]
[!EDITION-2021] 在 2021 edition 之前,可以省略
dyn关键字。在 2021 edition 及之后,从语义上要求使用dyn关键字。
r[type.trait-object.syntax-edition2018]
[!EDITION-2018] 在 2015 edition 中,
dyn后面必须跟着 [PathIdentSegment]、[LIFETIME_TOKEN]、for、(或?,才会被解释为关键字而不是普通标识符。最值得注意的是,
dyn、dyn::T和dyn<T>都会被当作类型路径。因此,若你想要一个以::module::Trait为 trait 的 trait 对象类型,需要把路径放在括号中,写作dyn (::module::Trait)。从 2018 edition 开始,
dyn是真正的关键字,不允许出现在路径中,因此不再需要括号。
r[type.trait-object.alias]
两个 trait 对象类型互为别名,当且仅当基础 trait 互为别名、自动 trait 集合相同、且生命周期约束相同。例如,dyn Trait + Send + UnwindSafe 与 dyn Trait + UnwindSafe + Send 相同。
r[type.trait-object.unsized]
由于值的具体类型是不透明的,trait 对象是动态大小类型。与所有 DST 一样,trait 对象用在某种指针之后;例如 &dyn SomeTrait 或 Box<dyn SomeTrait>。每个指向 trait 对象的指针实例包含:
- 一个指向实现了
SomeTrait的类型T的实例的指针 - 一张 虚方法表,通常简称为 vtable,其中对于
SomeTrait及其超 trait中T所实现的每个方法,都包含一个指向T的实现的指针(即函数指针)。
Trait 对象的目的是允许方法的「迟绑定」。在 trait 对象上调用方法会在运行时进行虚分派:也就是说,从 trait 对象的 vtable 中加载一个函数指针并间接调用。每个 vtable 条目的实际实现可以因对象而异。
一个 trait 对象的例子:
| |
在这个例子中,trait Printable 作为 trait 对象出现在 print 的类型签名以及 main 中的强制转换表达式中。
r[type.trait-object.lifetime-bounds]
Trait 对象的生命周期约束
由于 trait 对象可以包含引用,这些引用的生命周期需要作为 trait 对象的一部分来表达。该生命周期写作 Trait + 'a。存在一些默认值,使得这个生命周期通常可以被推断为一个合理的选择。