Update trait-object.md

pull/201/head
Jesse 3 years ago committed by GitHub
parent 5e1c52347b
commit d07bf788a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -43,7 +43,7 @@ fn draw(o: UiObject) {
}
```
Bingo这个确实是一个办法但是问题来了如果你的对象集合并不能事先明确地知道呢或者别人临时想要实现一个UI组件呢此时枚举中的类型是有些缺少的是不是还要修改你的代码增加一个枚举成员
Bingo这个确实是一个办法但是问题来了如果你的对象集合并不能事先明确地知道呢或者别人想要实现一个UI组件呢此时枚举中的类型是有些缺少的是不是还要修改你的代码增加一个枚举成员
总之在编写这个UI库时我们无法知道所有的UI对象类型只知道的是
- UI对象的类型不同
@ -137,7 +137,7 @@ fn main() {
```
上面代码,有几个非常重要的点:
- `draw1` 函数的参数是 `Box<dyn Draw>` 形式的特征对象,该特征对象是通过 `Box::new(x)` 的方式创建的我觉得这里应该贴一个dyn的链接不然此处是全书第一次接触这个概念
- `draw1` 函数的参数是 `Box<dyn Draw>` 形式的特征对象,该特征对象是通过 `Box::new(x)` 的方式创建的
- `draw2` 函数的参数是 `&dyn Draw` 形式的特征对象,该特征对象是通过 `&x` 的方式创建的
- `dyn` 关键字只用在特征对象的类型声明上,在创建时无需使用 `dyn`
@ -180,7 +180,7 @@ impl<T> Screen<T>
```
上面的 `Screen` 的列表中,存储了类型为 `T` 的元素,然后在 `Screen` 中使用特征约束让 `T` 实现了 `Draw` 特征,进而可以调用 `draw` 方法。
但是这种写法限制了 `Screen` 实例的 `Vec<T>` 中的每个元素必须全部`Button` 类型或者全是 `SelectBox` 类型。如果只需要同质(相同类型)集合,更倾向于这种写法:使用泛型和 特征约束,因为实现更清晰,且性能更好(特征对象,需要在运行时从 `vtable` 动态查找需要调用的方法vtable这个概念应该要讲一下我也是第一次听说
但是这种写法限制了 `Screen` 实例的 `Vec<T>` 中的每个元素必须是 `Button` 类型或者全是 `SelectBox` 类型。如果只需要同质(相同类型)集合,更倾向于这种写法:使用泛型和 特征约束,因为实现更清晰,且性能更好(特征对象,需要在运行时从 `vtable` 动态查找需要调用的方法。
现在来运行渲染下咱们精心设计的UI组件列表
```rust
@ -213,7 +213,7 @@ fn main() {
在上例中,`Screen` 在 `run` 的时候,我们并不需要知道各个组件的具体类型是什么。它也不检查组件到底是 `Button` 还是 `SelectBox` 的实例,只要它实现了 `Draw` 特征,就能通过 `Box::new` 包装成 `Box<dyn Draw>` 特征对象,然后被渲染在屏幕上。
使用特征对象和 Rust 类型系统来进行类似鸭子类型操作的优势是,无需在运行时检查一个值是否实现了特定方法或者担心在调用时因为值没有实现方法而产生错误(这里有点读不懂)。如果值没有实现特征对象所需的特征, 那么 Rust 根本就不会编译这些代码:
使用特征对象和 Rust 类型系统来进行类似鸭子类型操作的优势是,无需在运行时检查一个值是否实现了特定方法或者担心在调用时因为值没有实现方法而产生错误。如果值没有实现特征对象所需的特征, 那么 Rust 根本就不会编译这些代码:
```rust
fn main() {
@ -231,13 +231,13 @@ fn main() {
#### &dyn和Box\<dyn\>的区别
前文提到, `&dyn``Box<dyn>` 都可以用于特征对象,因此在功能上 `&dyn``Box<dyn>` 几乎没有区别,唯一的区别就是:`&dyn` 减少了一次指针调用。
因为 `Box<dyn>` 是一个宽(有的书上写的是胖?)指针(`fat pointer`),它内部保存一个指针指向 `vtable`,然后通过 `vtable` 查询到具体的函数指针,最后进行调用。
因为 `Box<dyn>` 是一个宽指针(`fat pointer`),它内部保存一个指针指向 `vtable`,然后通过 `vtable` 查询到具体的函数指针,最后进行调用。
所以,如果你在乎性能,又想使用特征对象简化代码,可以优先考虑 `&dyn`
注意 `dyn` 不能单独作为特征对象的定义,例如下面的代码编译器会报错,原因是特征对象可以是任意实现了某个特征的类型,编译器在编译期不知道该类型的大小,不同的类型大小是不同的。
`&dyn``Box<dyn>` 在编译期都是已知大小,大小为一个指针的长度,所以可以用作特征对象的定义。
`&dyn``Box<dyn>` 在编译期都是已知大小,所以可以用作特征对象的定义。
```rust
fn draw2(x: dyn Draw) {
@ -259,7 +259,7 @@ help: function arguments must have a statically known size, borrowed types alway
与静态分发相对应的是**动态分发(dynamic dispatch)**,在这种情况下,直到运行时,才能确定需要调用什么方法。
当使用特征对象时Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型所以它也不知道应该调用哪个类型的哪个方法实现。为此Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码(不是很理解这里,建议稍微展开一下让没学过编译原理的同学能读懂),这会相应的禁用一些优化。
当使用特征对象时Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型所以它也不知道应该调用哪个类型的哪个方法实现。为此Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码,这会相应的禁用一些优化。
## Self与self
在Rust中有两个`self`,一个指代当前的实例对象,一个指代特征或者方法类型的别名:

Loading…
Cancel
Save