diff --git a/src/basic/trait/trait-object.md b/src/basic/trait/trait-object.md index 7fff2378..049fdfc3 100644 --- a/src/basic/trait/trait-object.md +++ b/src/basic/trait/trait-object.md @@ -287,6 +287,18 @@ help: function arguments must have a statically known size, borrowed types alway +结合上文的内容和这张图可以了解: + +- **特征对象大小不固定**:这是因为,对于特征 `Draw`,类型 `Button` 可以实现特征 `Draw`,类型 `SelectBox` 也可以实现特征 `Draw`,因此特征没有固定大小 +- **几乎总是使用特征对象的引用方式**,如 `&dyn Draw`、`Box` + - 虽然特征对象没有固定大小,但它的引用类型的大小是固定的,它由两个指针组成(`ptr` 和 `vptr`),因此占用两个指针大小 + - 一个指针 `ptr` 指向实现了特征 `Draw` 的具体类型的实例,也就是当作特征 `Draw` 来用的类型的实例,比如类型 `Button` 的实例、类型 `SelectBox` 的实例 + - 另一个指针 `vptr` 指向一个虚表 `vtable`,`vtable` 中保存了类型 `Button` 或类型 `SelectBox` 的实例对于可以调用的实现于特征 `Draw` 的方法。当调用方法时,直接从 `vtable` 中找到方法并调用。之所以要使用一个 `vtable` 来保存各实例的方法,是因为实现了特征 `Draw` 的类型有多种,这些类型拥有的方法各不相同,当将这些类型的实例都当作特征 `Draw` 来使用时(此时,它们全都看作是特征 `Draw` 类型的实例),有必要区分这些实例各自有哪些方法可调用 + +简而言之,当类型 `Button` 实现了特征 `Draw` 时,类型 `Button` 的实例对象 `btn` 可以当作特征 `Draw` 的特征对象类型来使用,`btn` 中保存了作为特征对象的数据指针(指向类型 `Button` 的实例数据)和行为指针(指向 `vtable`)。 + +一定要注意,此时的 `btn` 被当作特征 `Draw` 的特征对象的实例数据,而不再是类型 `Button` 的实例对象,而且 `btn` 的 `vtable` 只包含了实现自特征 `Draw` 的那些方法(比如 `draw`),因此 `btn` 只能调用实现于特征 `Draw` 的 `draw` 方法,而不能调用类型 `Button` 本身实现的方法和类型 `Button` 实现于其他特征的方法。**也就是说,`btn` 当作哪个特征对象来用,它的 `vtable` 中就包含哪个特征的方法。** + ## Self 与 self 在 Rust 中,有两个`self`,一个指代当前的实例对象,一个指代特征或者方法类型的别名: @@ -361,4 +373,4 @@ error[E0038]: the trait `std::clone::Clone` cannot be made into an object ## 课后练习 -> [Rust By Practice](https://zh.practice.rs/generics-traits/trait-object.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。 \ No newline at end of file +> [Rust By Practice](https://zh.practice.rs/generics-traits/trait-object.html),支持代码在线编辑和运行,并提供详细的[习题解答](https://github.com/sunface/rust-by-practice)。