|
|
|
@ -8,9 +8,9 @@
|
|
|
|
|
|
|
|
|
|
### 关联类型在 trait 定义中指定占位符类型
|
|
|
|
|
|
|
|
|
|
**关联类型**(*associated types*)是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型。trait 的实现者会针对特定的实现在这个占位符类型指定相应的具体类型。如此可以定义一个使用多种类型的 trait,直到实现此 trait 时都无需知道这些类型具体是什么。
|
|
|
|
|
**关联类型**(*associated types*)让我们可以在 trait 里面增加一个待定义的类型(类型占位符),将类型占位符与 trait 相关联,这样 trait 的方法签名中就可以使用这些占位符类型。trait 的实现者在实现这个 trait 的时候,会指定一个具体类型,来替换掉这个占位符。这样,我们可以在一个 trait 中通过占位符使用不同类型,在实现此 trait 时才需要指定这些类型具体是什么。
|
|
|
|
|
|
|
|
|
|
本章所描述的大部分内容都非常少见。关联类型则比较适中;它们比本书其他的内容要少见,不过比本章中的很多内容要更常见。
|
|
|
|
|
我们之前提到,本章所描述的大部分内容都较少使用。关联类型则比较适中;它们比本书其他的内容要少见,不过比本章中的很多内容要更常见。
|
|
|
|
|
|
|
|
|
|
一个带有关联类型的 trait 的例子是标准库提供的 `Iterator` trait。它有一个叫做 `Item` 的关联类型来替代遍历的值的类型。`Iterator` trait 的定义如示例 19-12 所示:
|
|
|
|
|
|
|
|
|
@ -20,9 +20,10 @@
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 19-12: `Iterator` trait 的定义中带有关联类型 `Item`</span>
|
|
|
|
|
|
|
|
|
|
`Item` 是一个占位符类型,同时 `next` 方法定义表明它返回 `Option<Self::Item>` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,然而不管实现者指定何种类型,`next` 方法都会返回一个包含了此具体类型值的 `Option`。
|
|
|
|
|
`Item` 是一个占位符类型,同时 `next` 方法的定义表明它返回 `Option<Self::Item>` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,无论实现者指定何种类型,`next` 方法都会返回一个包含了此具体类型值的 `Option`。
|
|
|
|
|
|
|
|
|
|
关联类型看起来像一个类似泛型的概念,因为它允许定义一个函数而不指定其可以处理的类型。让我们通过在一个 `Counter` 结构体上实现 `Iterator` trait 的例子来检视其中的区别。这个实现中指定了 `Item` 的类型为 `u32`:
|
|
|
|
|
关联类型看起来有点像泛型:后者允许定义一个函数时,暂不指定其可以处理的类型。为了体现这两者的区别,请看下面的例子。
|
|
|
|
|
这个例子为 `Counter` 结构体实现了 `Iterator` trait,其中指定 `Item` 的类型为 `u32`:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名:src/lib.rs</span>
|
|
|
|
|
|
|
|
|
@ -30,7 +31,7 @@
|
|
|
|
|
{{#rustdoc_include ../listings/ch19-advanced-features/no-listing-22-iterator-on-counter/src/lib.rs:ch19}}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这个语法类似于泛型。那么为什么 `Iterator` trait 不像示例 19-13 那样定义呢?
|
|
|
|
|
这个语法类似于泛型。那么为什么 `Iterator` trait 不像下面示例 19-13 那样,使用泛型来定义呢?
|
|
|
|
|
|
|
|
|
|
```rust,noplayground
|
|
|
|
|
{{#rustdoc_include ../listings/ch19-advanced-features/listing-19-13/src/lib.rs}}
|
|
|
|
@ -40,9 +41,9 @@
|
|
|
|
|
|
|
|
|
|
区别在于当如示例 19-13 那样使用泛型时,则不得不在每一个实现中标注类型。这是因为我们也可以实现为 `Iterator<String> for Counter`,或任何其他类型,这样就可以有多个 `Counter` 的 `Iterator` 的实现。换句话说,当 trait 有泛型参数时,可以多次实现这个 trait,每次需改变泛型参数的具体类型。接着当使用 `Counter` 的 `next` 方法时,必须提供类型注解来表明希望使用 `Iterator` 的哪一个实现。
|
|
|
|
|
|
|
|
|
|
通过关联类型,则无需标注类型,因为不能多次实现这个 trait。对于示例 19-12 使用关联类型的定义,我们只能选择一次 `Item` 会是什么类型,因为只能有一个 `impl Iterator for Counter`。当调用 `Counter` 的 `next` 时不必每次指定我们需要 `u32` 值的迭代器。
|
|
|
|
|
有了关联类型,在实现时就无需标注类型,因为不能多次实现这个 trait。对于示例 19-12 使用关联类型的定义,我们只能选择一次 `Item` 会是什么类型,因为只能有一个 `impl Iterator for Counter`。当调用 `Counter` 的 `next` 时不必每次指定我们需要 `u32` 值的迭代器。
|
|
|
|
|
|
|
|
|
|
关联类型也会成为 trait 契约的一部分:trait 的实现必须提供一个类型来替代关联类型占位符。关联类型通常有一个描述类型用途的名字,并且在 API 文档中为关联类型编写文档是一个最佳实践。
|
|
|
|
|
关联类型也会成为 trait 契约的一部分:trait 的实现必须提供一个类型来替代关联类型占位符。关联类型通常以它的用途来命名,并且我们最好在 API 文档中为关联类型编写文档。
|
|
|
|
|
|
|
|
|
|
### 默认泛型类型参数和运算符重载
|
|
|
|
|
|
|
|
|
|