Update ch17-02-trait-objects.md

pull/49/head
Zheng Ping 8 years ago committed by GitHub
parent ae0aedbb12
commit 6e86edd5da

@ -258,14 +258,14 @@ objects. Clone is an example of one. You'll get errors that will let you know
if a trait can't be a trait object, look up object safety if you're interested if a trait can't be a trait object, look up object safety if you're interested
in the details"? Thanks! /Carol --> in the details"? Thanks! /Carol -->
不是所有的trait都可以被放进trait对象中; 只有*对象安全的*trait可以这样做. 一个trait只有同时满足如下两点时才被认为是对象安全的: 不是所有的trait都可以被放进trait对象中; 只有*对象安全的*trait可以这样做. 一个trait只有同时满足如下两点时才被认为是对象安全的:
* 该trait要求`Self`不是`Sized`; * 该trait要求`Self`不是`Sized`;
* 该trait的所有方法都是对象安全的; * 该trait的所有方法都是对象安全的;
`Self`是一个类型的别名关键字它表示当前正被实现的trait类型或者是方法所属的类型. `Sized`是一个像在第16章中介绍的`Send`和`Sync`样的标记trait, 在编译时它会自动被放进大小确定的类型里,比如`i32`和引用. 大小不确定的类型包括切片(`[T]`)和trait对象. `Self`是一个类型的别名关键字它表示当前正被实现的trait类型或者是方法所属的类型. `Sized`是一个像在第16章中介绍的`Send`和`Sync`样的标记trait, 在编译时它会自动被放进大小确定的类型里,比如`i32`和引用. 大小不确定的类型切片(`[T]`)和trait对象.
`Sized`是一个默认会被绑定到所有常规类型参数的内隐trait. Rust中要求一个类型是`Sized`的最具可用性的用法是让`Sized`成为一个默认的trait绑定这样我们就可以在大多数的常规的使用中不去写`T: Sized`了. 如果我们想在切片(slice)中使用一个trait, 我们需要取消对`Sized`的trait绑定, 我们只需制定`T: ?Sized`作为trait绑定. `Sized`是一个默认会被绑定到所有常规类型参数的内隐trait. Rust中要求一个类型是`Sized`的最具可用性的用法是让`Sized`成为一个默认的trait绑定这样我们就可以在大多数的常规的用中不去写`T: Sized`了. 如果我们想在切片(slice)中使用一个trait, 我们需要取消对`Sized`的trait绑定, 我们只需制定`T: ?Sized`作为trait绑定.
默认绑定到`Self: ?Sized`的trait可以被实现到是`Sized`或非`Sized`的类型上. 如果我们创建一个不绑定`Self: ?Sized`的trait`Foo`,它看上去应该像这样: 默认绑定到`Self: ?Sized`的trait可以被实现到是`Sized`或非`Sized`的类型上. 如果我们创建一个不绑定`Self: ?Sized`的trait`Foo`,它看上去应该像这样:
@ -281,27 +281,15 @@ Trait`Sized`现在就是trait`Foo`的一个*超级trait*, 也就是说trait`Foo`
第二点说对象安全要求一个trait的所有方法必须是对象安全的. 一个对象安全的方法满足下列条件: 第二点说对象安全要求一个trait的所有方法必须是对象安全的. 一个对象安全的方法满足下列条件:
* It requires `Self` to be `Sized` or * 它要求`Self`是`Sized`或者
* It meets all three of the following: * 它符合下面全部三点:
* It must not have any generic type parameters * 它不包含任意类型的常规参数
* Its first argument must be of type `Self` or a type that dereferences to * 它的第一个参数必须是类型`Self`或一个引用到`Self`的类型(也就是说它必须是一个方法而非关联函数并且以`self`、`&self`或`&mut self`作为第一个参数)
the Self type (that is, it must be a method rather than an associated * 除了第一个参数外它不能在其它地方用`Self`作为方法的参数签名
function and have `self`, `&self`, or `&mut self` as the first argument)
* It must not use `Self` anywhere else in the signature except for the 虽然这些规则有一点形式化, 但是换个角度想一下: 如果你的方法在它的参数签名的其它地方也需要具体的`Self`类型参数, 但是一个对象又忘记了它的具体类型是什么, 这时该方法就无法使用被它忘记的原先的具体类型. 当该trait被使用时, 被具体类型参数填充的常规类型参数也是如此: 这个具体的类型就成了实现该trait的类型的某一部分, 如果使用一个trait对象时这个类型被抹掉了, 就没有办法知道该用什么类型来填充这个常规类型参数.
first argument
一个trait的方法不是对象安全的一个例子是标准库中的`Clone`trait. `Clone`trait的`clone`方法的参数签名是这样的:
Those rules are a bit formal, but think of it this way: if your method requires
the concrete `Self` type somewhere in its signature, but an object forgets the
exact type that it is, there's no way that the method can use the original
concrete type that it's forgotten. Same with generic type parameters that are
filled in with concrete type parameters when the trait is used: the concrete
types become part of the type that implements the trait. When the type is
erased by the use of a trait object, there's no way to know what types to fill
in the generic type parameters with.
An example of a trait whose methods are not object safe is the standard
library's `Clone` trait. The signature for the `clone` method in the `Clone`
trait looks like this:
```rust ```rust
pub trait Clone { pub trait Clone {
@ -309,21 +297,11 @@ pub trait Clone {
} }
``` ```
`String` implements the `Clone` trait, and when we call the `clone` method on `String`实现了`Clone` trait, 当我们在一个`String实例上调用`clone`方法时, 我们会得到一个`String`实例. 同样地, 如果我们在一个`Vec`实例上调用`clone`方法, 我们会得到一个`Vec`实例. `clone`的参数签名需要知道`Self`是什么类型, 因为它需要返回这个类型.
an instance of `String` we get back an instance of `String`. Similarly, if we
call `clone` on an instance of `Vec`, we get back an instance of `Vec`. The
signature of `clone` needs to know what type will stand in for `Self`, since
that's the return type.
If we try to implement `Clone` on a trait like the `Draw` trait from Listing 如果我们想在像17-3中列出的`Draw`trait那样的trait上实现`Clone`, 我们就不知道`Self`将会是一个`Button`, 一个`SelectBox`, 或者是其它的在将来要实现`Draw`trait的类型.
17-3, we wouldn't know whether `Self` would end up being a `Button`, a
`SelectBox`, or some other type that will implement the `Draw` trait in the
future.
The compiler will tell you if you're trying to do something that violates the 如果你做了违反trait对象的对象安全性规则的事情, 编译器将会告诉你. 比如, 如果你实现在17-4中列出的`Screen`结构, 你想让该结构像这样持有实现了`Clone`trait的类型而不是`Draw`trait:
rules of object safety in regards to trait objects. For example, if we had
tried to implement the `Screen` struct in Listing 17-4 to hold types that
implement the `Clone` trait instead of the `Draw` trait, like this:
```rust,ignore ```rust,ignore
pub struct Screen { pub struct Screen {
@ -331,7 +309,7 @@ pub struct Screen {
} }
``` ```
We'll get this error: 我们将会得到下面的错误:
```text ```text
error[E0038]: the trait `std::clone::Clone` cannot be made into an object error[E0038]: the trait `std::clone::Clone` cannot be made into an object

Loading…
Cancel
Save