Modify content in trait-object.md

pull/117/head
lijinpeng 3 years ago
parent 40f4c67bff
commit e09a78c544

@ -217,20 +217,38 @@ fn main() {
```
因为`String`类型没有实现`Draw`特征,编译器直接就会报错,不会让上述代码运行。如果想要`String`类型被渲染在屏幕上,那么只需要为其实现`Draw`特征即可,非常容易。
#### &和dyn的区别
前文提到,`&`和`dyn`都可以用于特征对象,因此在功能上`&`和`dyn`几无区别,唯一的区别就是:`&`减少了一次指针调用。
#### &dynBox\<dyn\>的区别
前文提到,`&dyn`和`Box<dyn>`都可以用于特征对象,因此在功能上`&dyn`和`Box<dyn>`几无区别,唯一的区别就是:`&dyn`减少了一次指针调用。
因为`dyn`是一个宽指针(`fat pointer`), 它内部保存一个指针指向`vtable`,然后通过`vtable`查询到具体的函数指针,最后进行调用.
因为`Box<dyn>`是一个宽指针(`fat pointer`), 它内部保存一个指针指向`vtable`,然后通过`vtable`查询到具体的函数指针,最后进行调用.
所以,如果你在乎性能,又想使用特征对象简化代码,可以优先考虑`&`。
所以,如果你在乎性能,又想使用特征对象简化代码,可以优先考虑`&dyn`。
注意`dyn`不能单独作为特征对象的定义,例如下面的代码编译器会报错,原因是特征对象可以是任意实现了某个特征的类型,编译器在编译期不知道该类型的大小。
而`&dyn`和`Box<dyn>`在编译期都是已知大小,所以可以用作特征对象的定义。
```rust
fn draw2(x: dyn Draw) {
x.draw();
}
```
```
10 | fn draw2(x: dyn Draw) {
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Draw + 'static)`
help: function arguments must have a statically known size, borrowed types always have a known size
```
## 特征对象的动态分发
回一下泛型章节我们提到过的,泛型是在编译期完成处理的:编译器会为每一个泛型参数对应的具体类型生成一份代码,这种方式是**静态分发(static dispatch)**,因为是在编译期完成的,对于运行期性能完全没有任何影响。
一下泛型章节我们提到过的,泛型是在编译期完成处理的:编译器会为每一个泛型参数对应的具体类型生成一份代码,这种方式是**静态分发(static dispatch)**,因为是在编译期完成的,对于运行期性能完全没有任何影响。
与静态分发相对应的是**动态分发(dynamic dispatch)**,在这种情况下,直到运行时,才能确定需要调用什么方法。
当使用特赠对象时Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型所以它也不知道应该调用哪个类型的哪个方法实现。为此Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码,这会相应的禁用一些优化。
当使用特对象时Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型所以它也不知道应该调用哪个类型的哪个方法实现。为此Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码,这会相应的禁用一些优化。
## Self与self
在Rust中有两个`self`,一个指代当前的实例对象,一个指代特征或者方法类型的别名:
@ -264,7 +282,7 @@ fn main() {
对象安全对于特征对象是必须的,因为一旦有了特征对象,就不再知道实现该特征的具体类型是什么了。如果特征方法返回具体的`Self`类型,但是特征对象忘记了其真正的类型,那这个`Self`就非常尴尬,因为没人知道它是谁了。同理对于泛型类型参数来说,当使用特征时其会放入具体的类型参数:此具体类型变成了实现该特征的类型的一部分。当使用特征对象时其具体类型被抹去了,故而无从得知放入泛型参数类型到底是什么。
标准库中的 `Clone`特征就符合对象安全的要求:
标准库中的 `Clone`特征就符合对象安全的要求:
```rust
pub trait Clone {

Loading…
Cancel
Save