From d4351aac10e7629ece59be697789b6b58817e112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=87=91=E6=99=96?= <55843852+pphui8@users.noreply.github.com> Date: Sun, 20 Mar 2022 12:36:16 +0800 Subject: [PATCH] 17-2 add object safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 我注意到17-2章最后有一小节没有翻译,就先翻译上了。 我不太清楚把新加的实例代码放在哪里,就直接放在文档中了。 I`m not sure where to push the new demonstrate code so I just put them in origin file --- src/ch17-02-trait-objects.md | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/ch17-02-trait-objects.md b/src/ch17-02-trait-objects.md index 57b5c4f..28c7ed5 100644 --- a/src/ch17-02-trait-objects.md +++ b/src/ch17-02-trait-objects.md @@ -126,6 +126,56 @@ 当使用 trait 对象时,Rust 必须使用动态分发。编译器无法知晓所有可能用于 trait 对象代码的类型,所以它也不知道应该调用哪个类型的哪个方法实现。为此,Rust 在运行时使用 trait 对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码,这会相应的禁用一些优化。尽管在编写示例 17-5 和可以支持示例 17-9 中的代码的过程中确实获得了额外的灵活性,但仍然需要权衡取舍。 +### trait对象需要类型安全 + +只有对象安全(object-safe)的trait可以实现为特征对象。这里有一些复杂的规则来实现trait的对象安全,但在实践中,只有两个相关的规则。如果一个 trait 中定义的所有方法都符合以下规则,则该 trait 是对象安全的: + +- 返回值不是 `Self` +- 没有泛型类型的参数 + +`Self` 关键字是我们在 trait 与方法上的实现的别称,trait 对象必须是对象安全的,因为一旦使用 trait 对象,Rust 将不再知晓该实现的返回类型。如果一个 trait 的方法返回了一个 `Self` 类型,但是该 trait 对象忘记了 `Self` 的确切类型,那么该方法将不能使用原本的类型。当 trait 使用具体类型填充的泛型类型时也一样:具体类型成为实现 trait 的对象的一部分,当使用 trait 对象却忘了类型是什么时,无法知道应该用什么类型来填充泛型类型。 + +一个非对象安全的 trait 例子是标准库中的 `Clone` trait。`Clone` trait 中的 `clone` 方法的声明如下: + +```rust,ignore +pub trait Clone { + fn clone(&self) -> Self; +} +``` + +`String` 类型实现了 `Clone` trait,当我们在 `String` 的实例对象上调用 `clone` 方法时,我们会得到一个 `String` 类型实例对象。相似地,如果我们调用 `Vec` 实例对象上的 `clone` 方法,我们会得到一个 `Vec` 类型的实例对象。`clone` 方法的标签需要知道哪个类型是 `Self` 类型,因为 `Self` 是它的返回类型。 + +当我们尝试编译一些违反 trait 对象的对象安全规则的代码时,我们会收到编译器的提示。例如,我们想实现17-4的 `Screen` 结构体来保存一个实现了 `Clone` trait 而不是 `Draw` trait 的类型,如下所示 + +```rust,ignore,does_not_compile +pub struct Screen { + pub components: Vec>, +} +``` + +我们将会收到如下错误: + +```console +$ cargo build + Compiling gui v0.1.0 (file:///projects/gui) +error[E0038]: the trait `Clone` cannot be made into an object + --> src/lib.rs:2:29 + | +2 | pub components: Vec>, + | ^^^^^^^^^ `Clone` cannot be made into an object + | + = note: the trait cannot be made into an object because it requires `Self: Sized` + = note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + +For more information about this error, try `rustc --explain E0038`. +error: could not compile `gui` due to previous error +``` + +这个错误意味着我们不能将此 trait 用于 trait 对象。如果你想了解更多有关对象安全的细节,请移步至 [Rust RFC 255][Rust RFC 255 ref] 或查看 [Rust Reference][Rust Reference ref] + + [performance-of-code-using-generics]: ch10-01-syntax.html#泛型代码的性能 [dynamically-sized]: ch19-04-advanced-types.html#动态大小类型和-sized-trait +[Rust RFC 255 ref]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md +[Rust Reference ref]: https://doc.rust-lang.org/reference/items/traits.html#object-safety \ No newline at end of file