mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 12:43:28 +08:00
Updated 'src/advanced_features/traits.md'.
This commit is contained in:
@@ -1,23 +1,5 @@
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct Point {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
impl Add for Point {
|
||||
type Output = Point;
|
||||
|
||||
fn add(self, other: Point) -> Point {
|
||||
Point {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct Millimeters(u32);
|
||||
|
||||
@@ -33,13 +15,8 @@ impl Add<Meters> for Millimeters {
|
||||
}
|
||||
|
||||
fn main () {
|
||||
assert_eq! (
|
||||
Point { x: 1, y: 0 } + Point { x: 2, y: 3},
|
||||
Point { x: 3, y: 3 }
|
||||
);
|
||||
let a = Millimeters(100);
|
||||
let b = Meters(3);
|
||||
|
||||
assert_eq! (
|
||||
Millimeters(50) + Meters(1),
|
||||
Millimeters(1050)
|
||||
);
|
||||
println! ("{:?}", a + b);
|
||||
}
|
||||
|
||||
6
projects/traits_example/Cargo.toml
Normal file
6
projects/traits_example/Cargo.toml
Normal file
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "traits_example"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
34
projects/traits_example/src/main.rs
Normal file
34
projects/traits_example/src/main.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
trait Pilot {
|
||||
fn fly(&self);
|
||||
}
|
||||
|
||||
trait Wizard {
|
||||
fn fly(&self);
|
||||
}
|
||||
|
||||
struct Human;
|
||||
|
||||
impl Pilot for Human {
|
||||
fn fly(&self) {
|
||||
println! ("这是你们的机长在讲话。");
|
||||
}
|
||||
}
|
||||
|
||||
impl Wizard for Human {
|
||||
fn fly(&self) {
|
||||
println! ("上升!");
|
||||
}
|
||||
}
|
||||
|
||||
impl Human {
|
||||
fn fly(&self) {
|
||||
println! ("*疯狂地挥舞双臂*");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let person = Human;
|
||||
Pilot::fly(&person);
|
||||
Wizard::fly(&person);
|
||||
person.fly();
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
# 高级特质
|
||||
|
||||
在第 10 章 [“特质:定义共用行为”](Ch10_Generic_Types_Traits_and_Lifetimes.md#特质定义共用行为) 小节中,咱们曾首先涉及到特质,但咱们不曾讨论更为高级的那些细节。现在咱们对 Rust 有了更多了解,咱们就可以深入本质,get into the nitty-gritty。
|
||||
我们在第 10 章中 [以特质定义共用行为](../generic_types_traits_and_lifetimes/traits.md) 小节中首次介绍了特质,但我们未曾讨论一些更高级的细节。现在咱们对 Rust 有了更深入的了解,我们就可以深入探讨其中的细节了。
|
||||
|
||||
|
||||
## 使用关联类型指定出特质定义中的一些占位性类型
|
||||
## 通过关联类型定义特质
|
||||
|
||||
**Specifying placeholder types in trait definitions with associated types**
|
||||
所谓 *关联类型*,将类型占位符与特质连接,使得特质方法的定义可以在其签名中使用这些占位符类型。特质的实现者将针对特定实现,指定要使用的具体类型以取代占位符类型。这样,我们就可以无需在特质被实现之前确切地知道类型是什么下,定义一个使用某些类型的特质。
|
||||
|
||||
在本章中,我们提到大多数高级特性很少被用到。关联类型处于中间位置:他们的使用频率虽低于本书其余部分中介绍的特性,但比这一章中讨论的其他特性更为常用。
|
||||
|
||||
*关联类型* 将类型占位符与特质加以结合,从而那些特质方法的定义,就可以在他们的签名中,使用这些占位符类型。特质的实现者,将为其特定实现,指明占位符类型所要使用的具体类型。如此一来,咱们便可以在特质被实现之前,无需准确获悉特质用到的类型下,定义出用到这些类型的特质。
|
||||
|
||||
在本章中,咱们已经介绍了绝大多数极少需要用到的高级特性。而关联类型则是位于这些高级特性中部的一种:其相较本书其余部分降到的那些特性,用得尤其少见,但相较这一章中讨论到的其他特性,则其要更常用一些。
|
||||
|
||||
带有关联类型特质的一个示例,便是标准库所提供的 `Iterator` 特质。其中的关联类型名为 `Item`,且代表着实现了这个 `Iterator` 特质的类型所迭代的那些值的类型。`Iterator` 特质的定义如下清单 19-12 中所示。
|
||||
|
||||
一个带有关联类型特质的示例是,标准库提供的 `Iterator` 特质。其中的关联类型名为 `Item`,代表着实现 `Iterator` 特质的类型正在迭代的值的类型。`Iterator` 特质的定义如下清单 20-13 中所示。
|
||||
|
||||
<a name="listing_20-13"></a>
|
||||
```rust
|
||||
pub trait Iterator {
|
||||
type Item;
|
||||
@@ -23,11 +20,11 @@ pub trait Iterator {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-12:有着关联类型 `Item` 的 `Iterator` 特质的定义*
|
||||
**清单 20-13**:有着关联类型 `Item` 的 `Iterator` 特质的定义
|
||||
|
||||
其中的类型 `Item` 便是个占位符,而那个 `next` 方法的定义,则显示其将返回类型类型为 `Option<Self::Item>` 的值。`Iterator` 的实现者,将指明 `Item` 的具体类型,同时 `next` 方法将返回包含那个具体类型值的一个 `Option`。
|
||||
类型 `Item` 是个占位符,而 `next` 方法的定义表明他将返回类型为 `Option<Self::Item>` 的值。`Iterator` 特质的实现者将指定 `Item` 的具体类型,而 `next` 方法将返回包含一个包含该具体类型值的 `Option`。
|
||||
|
||||
从泛型允许咱们在不指明函数可处理何种类型下,而定义出某个函数上看,关联类型可能看起来是个与泛型类似类似的概念。为检视这两个概念的不同,咱们将看看在指定了 `Item` 类型为 `u32` 的一个名为 `Counter` 的类型上的 `Iterator` 实现:
|
||||
关联类型可能看起来是个与泛型类似的概念,因为后者允许我们在不指定函数可以处理哪些类型下定义函数。为了探讨这两个概念之间的区别,我们将看看对名为 `Counter` 的类型的一种 `Iterator` 特质的实现,该实现指定 `Item` 类型为 `u32`:
|
||||
|
||||
```rust
|
||||
impl Iterator for Counter {
|
||||
@@ -37,7 +34,7 @@ impl Iterator for Counter {
|
||||
// --跳过代码--
|
||||
```
|
||||
|
||||
这种语法似乎可与泛型的那种语法相比。那么为何没有只使用泛型定义 `Iterator`,如下清单 19-13 中所示的那样呢?
|
||||
这种语法看起来与泛型相当。那么,为什么不直接如下清单 20-14 中所示那样,使用使用泛型定义 `Iterator` 呢?
|
||||
|
||||
```rust
|
||||
pub trait Iterator<T> {
|
||||
@@ -45,32 +42,26 @@ pub trait Iterator<T> {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-13:使用泛型的一种 `Iterator` 特质的定义假设*
|
||||
**清单 20-14**:使用泛型对 `Iterator` 特质的假设定义
|
||||
|
||||
不同之处在于,如同清单 19-13 中使用泛型时,咱们必须注解每个实现中的那些类型;由于咱们还可以实现 `Iterfator<String> for Counter` 或任何其他类型,咱们可以有对 `Counter` 的多个 `Iterator` 实现。也就是说,在特质有着泛型参数时,他就可以对某个类型被实现多次,每次都修改泛型参数的具体类型。当在 `Counter` 上使用 `next` 方法时,咱们就将不得不提供类型注解,来表明咱们想要使用哪个 `Iterator` 实现。
|
||||
区别在于,当使用泛型时,如清单 20-14 中那样,我们必须在每个实现中注解类型;由于我们还可以针对 `Counter` 实现 `Iterfator<String>` 或任何其他类型,因此我们可以为 `Counter` 提供多个 `Iterator` 实现。换句话说,当特质包含泛型参数时,他可以针对一个类型被实现多次,每次都修改泛型类型参数的具体类型。当我们对 `Counter` 使用 `next` 方法时,我们将必须提供类型注解,来表明我们打算使用的 `Iterator` 实现。
|
||||
|
||||
而在关联类型下,由于我们无法在一个类型上多次实现某个特质,因此就无需注解类型。在上面有着用到关联类型定义的清单 9-12 中,由于只能有一个 `impl Iterator for Counter`,所以咱们就只能就`Item` 为何选择一次。咱们不必在 `Counter` 上调用 `next` 的每个地方,指定咱们所要的是个 `u32` 值的迭代器。
|
||||
在关联类型下,我们不需要类型注解,因为我们无法对同一个类型多次实现某个特质。在有着使用关联类型的定义的清单 20-13 中,我们只能选择一次 `Item` 的类型,因为只能有一个 `impl Iterator for Counter`。我们不必在对 `Counter` 调用 `next` 的每个地方,都指定我们想要一个 `u32` 值的迭代器。
|
||||
|
||||
关联类型也成为了特质合约的一部分:特质的实现者必须提供一种类型,来代替关联类型的占位符。关联类型通常有着一个描述其使用方式的名字,并且在 API 文档中记录关联类型是一种很好的做法。
|
||||
|
||||
|
||||
关联类型还成了特质合约的一部分:特质的实现着必须提供一种类型,来顶替那个关联类型占位符。关联类型通常会有个描述该类型将被如何使用的名字,而在 API 文档中对关联类型编写文档,则是良好的做法。
|
||||
## 使用默认泛型类型参数和运算符重载
|
||||
|
||||
> **译注**:请参考 [Difference Between Method Overloading and Method Overriding in Java](https://www.geeksforgeeks.org/difference-between-method-overloading-and-method-overriding-in-java/) 了解 Java 中的重载与重写的区别。
|
||||
|
||||
## 默认泛型参数与运算符的重载
|
||||
在泛型类型参数时,我们可以为泛型类型指定一种默认的具体类型。当默认类型有效时,这样做消除了特质的实现者指定具体类型的需要。在声明泛型类型时,咱们可以通过 `<PlaceholderType=ConcreteType>` 语法指定默认类型。
|
||||
|
||||
**Default Generic Type Parameters and Operator Overloading**
|
||||
|
||||
|
||||
> **注**:请参考 [Difference Between Method Overloading and Method Overriding in Java](https://www.geeksforgeeks.org/difference-between-method-overloading-and-method-overriding-in-java/) 了解 Java 中的重载与重写区别。
|
||||
|
||||
在咱们用到泛型参数时,咱们可以给泛型指定默认具体类型。在所指定的默认类型就有效时,这样做消除了实现者指定具体类型的需求。在声明泛型时使用 `<PlaceholderType=ConcreteType>` 语法,指定出默认类型。
|
||||
|
||||
|
||||
这种技巧有用处情形的一个了不起示例,便是 *运算符重载,operator overloading*,咱们可以其在某些情形下,定制某个运算符(比如 `+`)的行为。
|
||||
|
||||
|
||||
Rust 不允许咱们创建自己的运算符,或重载任意运算符。但咱们可以通过实现与运算符相关的特质,而重载那些运算及于 `std::ops` 中所列出的相应特质。比如,在下面清单 19-14 中,咱们就将 `+` 运算符过载为把两个 `Point` 实例加在一起。咱们是通过在 `Point` 结构体上实现 `Add` 特质完成这一点的。
|
||||
这种技巧有用的一个很好的示例是 *运算符重载*,其中咱们可以在特定情形下自定义运算符(比如 `+`)的行为。
|
||||
|
||||
Rust 不允许咱们创建自己的运算符,或重载任意运算符。但咱们可以通过实现与运算符相关的特质,来重载 `std::ops` 中列出的操作和对应的特质。例如,在下面清单 20-15 中,我们重载了 `+` 运算符,以将两个 `Point` 实例相加。我们通过对 `Point` 结构体实现 `Add` 特质来实现这点。
|
||||
|
||||
<a name="listing_20-15"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -101,12 +92,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-14:实现 `Add` 特质来为 `Point` 实例过载 `+` 运算符*
|
||||
**清单 20-15**:实现 `Add` 特质,以针对`Point` 实例重载 `+` 运算符
|
||||
|
||||
这里的 `add` 方法,将两个 `Point` 实例的 `x` 值及两个实例的 `y` 值相加,而创建出一个新的 `Point`。这里的 `Add` 特质有着一个确定自其中的 `add` 方法返回类型,名为的 `Output` 关联类型。
|
||||
|
||||
此代码中的默认泛型,是在 `Add` 特质里。以下便是其定义:
|
||||
其中 `add` 方法会将两个 `Point` 实例的 `x` 值和两个实例的 `y` 值相加,从而创建一个新的 `Point`。`Add` 特质有个名为 `Output` 的关联类型,确定从 `add` 方法返回的类型。
|
||||
|
||||
这段代码中的默认泛型类型位于 `Add` 特质内部。以下是其定义:
|
||||
|
||||
```rust
|
||||
trait Add<Rhs=Self> {
|
||||
@@ -116,15 +106,16 @@ trait Add<Rhs=Self> {
|
||||
}
|
||||
```
|
||||
|
||||
此代码看起来应相当熟悉:有着一个方法与关联类型的特质。其中新的部分为 `Rhs=Self`:这种语法叫做 *默认类型参数,default type parameters*。其中的 `Rhs` 泛型参数(是 `right hand side` 的缩写),定义了 `add` 方法中 `rhs` 参数的类型,当咱们实现这个 `Add` 特质,而没有指明 `Rhs` 的类型时,`Rhs` 的类型将默认为 `Self`,其将是咱们在其上实现 `Add` 的类型。
|
||||
这段代码看起来应该熟悉:一个包含一个方法和关联类型的特质。新的部分是 `Rhs=Self`:这种语法叫做 *默认类型参数*。其中 `Rhs` 泛型类型参数(`right hand side` 的缩写),定义了 `add` 方法中 `rhs` 参数的类型。当我们在实现 `Add` 特质时没有为 `Rhs` 指定具体类型时,`Rhs` 的类型将默认为 `Self`,即我们正在对其实现 `Add` 的类型。
|
||||
|
||||
当咱们为 `Point` 实现 `Add` 时,由于咱们打算把两个 `Point` 实例相加,因此而使用了 `Rhs` 的默认值。接下来看看,其中咱们打算定制那个 `Rhs` 而非使用其默认值的一个 `Add` 实现示例。
|
||||
|
||||
我们有着两个结构体,`Millimeters` 与 `Meters`,保存不同单位的值。这种在另一类型中对现有类型的简单封装,成为 *新类型模式,newtype pattern*,我们会在 [通过新型模式实现外部特质](#通过新型模式实现外部特质) 小节对此进行更详细的说明。我们打算将以毫米为单位的值,与以米为单位的值相加,并要让 `Add` 的实现正确地进行单位转换。我们通过将 `Meters` 作为 `Rhs`,对 `Millimeters` 实现 `Add`,如下清单 20-16 中所示。
|
||||
|
||||
咱们有着两个结构体,`Millimeters` 与 `Meters`,保存着不同单位的一些值。这种将某个既有类型,封装在另一结构体的瘦封装,thin wrapping,就叫做 *新类型模式,newtype pattern*,在后面的 [“使用新型模式在外部类型上实现外部特质”](#使用新型模式在外层类型上实现外层的特质) 小节,咱们会对其进行更深入讨论。咱们打算把毫米值与以米计数的值相加,并要让 `Add` 的实现,正确完成单位转换。咱们可在将 `Meters` 作为 `Rhs` 下,对 `Millimeters` 实现 `Add`,如下清单 19-15 中所示。
|
||||
|
||||
|
||||
<a name="listing_20-16"></a>
|
||||
```rust
|
||||
use std::ops::Add;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct Millimeters(u32);
|
||||
|
||||
@@ -140,30 +131,27 @@ impl Add<Meters> for Millimeters {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-15:在 `Millimeters` 上实现 `Add` 特质,以将 `Millimeters` 与 `Meters` 相加*
|
||||
**清单 20-16**:对 `Millimeters` 实现 `Add` 特质,以将 `Millimeters` 与 `Meters` 相加
|
||||
|
||||
为了将 `Millimeters` 与 `Meters` 相加,咱们指明了 `impl Add<Meters>` 来设置那个 `Rhs` 类型参数,而非使用其默认的 `Self`。
|
||||
为了将 `Millimeters` 和 `Meters` 相加,我们指定 `impl Add<Meters>` 以设置 `Rhs` 类型参数的值,而不是使用其默认的 `Self`。
|
||||
|
||||
咱们将以如下两种主要方式,使用默认的类型参数:
|
||||
咱们将以如下两种主要方式,使用默认类型参数:
|
||||
|
||||
- 在不破坏既有代码之下,扩展某个类型;
|
||||
- 为实现绝大多数不会需要的特定情形下的定制,to allow customization in specific cases most users won't need。
|
||||
1. 在不破坏现有代码的情况下,扩展某个类型;
|
||||
2. 允许在大多数用户不需要的特定情形下进行自定义。
|
||||
|
||||
标准库的 `Add` 特质就是第二种用途的一个示例:通常,咱们将把两个相似的类型相加,但 `Add` 特质提供了超越这种情况的自定义能力。在 `Add` 特质中使用默认类型意味着,在大多数时候咱们不必指定额外的参数。换句话说,部分实现样板代码不再需要,从而使得更容易使用该特质。
|
||||
|
||||
第一个用途与第二种类似,但方向相反:当咱们打算添加类型参数到某个现有特质时,咱们可以给予他一个默认类型参数,从而在不破坏现有实现代码的情况下,扩展该特质的功能。
|
||||
|
||||
|
||||
标准库的 `Add` 特质,便是第二种目的的一个示例:通常,咱们将把两个相似类型相加,但 `Add` 特质提供了定制超出那种情况的能力。在 `Add` 特质中使用默认类型,就意味着咱们不必在多数时候指定额外的参数。换句话说,并不需要一点点的实现样板,从而令到使用这个特质更为容易。
|
||||
## 消除同名方法之间的歧义
|
||||
|
||||
第一个目的与第二个类似,不过是反过来的:在咱们打算将类型参数添加到某个既有特质时,就可以给到其一个默认值,从而在不破坏既有那些实现代码下,实现该特质功能的扩展。
|
||||
Rust 并未禁止一个特质有着与另一特质的方法同名的方法,也不会阻止咱们对同一个类型实现这两个特质。咱们还可以对类型实现与特质中方法同名的方法。
|
||||
|
||||
在调用名字相同的方法时,咱们需要告诉 Rust 希望使用哪个。请考虑下面清单 20-17 中的代码,其中我们定义了两个特质 `Pilot` 和 `Wizard`,他们都包含一个名为 `fly` 的方法。然后,我们对 `Human` 类型实现了这两个特质,该类型本身已经实现了一个名为 `fly` 的方法。每个 `fly` 方法都执行不同的操作。
|
||||
|
||||
## 用于消除歧义的完全合格语法:以同一名字调用方法
|
||||
|
||||
**Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name**
|
||||
|
||||
|
||||
Rust 中没有什么可以阻止某个特质有着,与另一特质的方法同样名字的方法,Rust 也不会阻止咱们在一个类型上实现这两种特质。至于直接在类型上,以来自不同特质方法的同样名字实现方法,也是可行的。
|
||||
|
||||
在以同一名字调用这些方法时,咱们将需要告诉 Rust 打算使用哪一个。设想下面清单 19-16 中,定义了两个特质,`Pilot` 与 `Wizard`,两个特质都有一个叫做 `fly` 的代码。咱们随后在已在其上实现了一个名为 `fly` 方法的类型 `Human` 上,实现了这两个特质。每个 `fly` 都完成不同的事情。
|
||||
|
||||
<a name="listing_20-17"></a>
|
||||
```rust
|
||||
trait Pilot {
|
||||
fn fly(&self);
|
||||
@@ -177,27 +165,28 @@ struct Human;
|
||||
|
||||
impl Pilot for Human {
|
||||
fn fly(&self) {
|
||||
println! ("机长在此发言。");
|
||||
println! ("这是你们的机长在讲话。");
|
||||
}
|
||||
}
|
||||
|
||||
impl Wizard for Human {
|
||||
fn fly(&self) {
|
||||
println! ("飞起来!");
|
||||
println! ("上升!");
|
||||
}
|
||||
}
|
||||
|
||||
impl Human {
|
||||
fn fly(&self) {
|
||||
println! ("*愤怒地挥动双臂*");
|
||||
println! ("*疯狂地挥舞双臂*");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-16:两个被定义作有 `fly` 方法的特质并都在 `Human` 类型上被实现,且在 `Human` 上直接实现了一个 `fly` 方法*
|
||||
**清单 20-17**:两个特质被定义为有着 `fly` 方法,并得以对 `Human` 类型实现,以及一个直接在 `Human` 上实现的 `fly` 方法
|
||||
|
||||
当咱们在 `Human` 实例上调用 `fly` 时,编译器默认为调用直接在该类型上实现的那个方法,如下清单 19-17 中所示。
|
||||
当我们对 `Human` 实例调用 `fly` 时,编译器默认调用直接在该类型上实现的方法,如下清单 20-18 中所示。
|
||||
|
||||
<a name="listing_20-18"></a>
|
||||
```rust
|
||||
fn main() {
|
||||
let person = Human;
|
||||
@@ -205,12 +194,13 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-17:调用 `Human` 实例上的 `fly`*
|
||||
**清单 20-18**:对 `Human` 实例调用 `fly`
|
||||
|
||||
运行此代码将打印出 `*愤怒地挥动双臂*`,显示 Rust 调用了直接在 `Human` 上实现的那个 `fly` 方法。
|
||||
运行这段代码将打印 `*疯狂地挥舞双臂*`,表明 Rust 调用了直接定义在 `Human` 上的 `fly` 方法。
|
||||
|
||||
为了调用 `Pilot` 或 `Wizard` 特质上的 `fly` 方法,咱们需要使用更为显式的语法,来指明我们所指的是那个 `fly` 方法。下面清单 19-18 对此语法进行了演示。
|
||||
为了调用 `Pilot` 或 `Wizard` 特质中的 `fly` 方法,我们需要使用更明确的语法,来指定我们指的是哪个 `fly` 方法。下面清单 20-19 演示了这种语法。
|
||||
|
||||
<a name="listing_20-19"></a>
|
||||
文件名:`src/main.rs`
|
||||
|
||||
```rust
|
||||
@@ -222,22 +212,20 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 19-18:指明咱们打算调用哪个特质的 `fly` 方法*
|
||||
**清单 20-19**:指定我们打算调用哪个特质的 `fly` 方法
|
||||
|
||||
在方法名字前指定特质名字,可以向 Rust 澄清我们打算调用的 `fly` 的实现。我们原本可以写下 `Human::fly(&person)`,这等同于我们在清单 20-19 中使用的 `person.fly()`,但若我们不需要消除歧义,那么这种写法稍微冗长一些。
|
||||
|
||||
在方法名字前指明特质名字,就向 Rust 澄清了咱们打算调用 `fly` 的哪个实现。咱们本来也可以写下 `Human::fly(&person)`,这与咱们曾在清单 19-18 中所使用的 `person.fly()` 等级,但若咱们无需消除歧义,这样写起来就些许有些长了。
|
||||
|
||||
|
||||
运行此代码会打印以下输出:
|
||||
|
||||
运行这段代码会打印以下内容:
|
||||
|
||||
```console
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
|
||||
Running `target/debug/disambiguation`
|
||||
机长在此发言。
|
||||
飞起来!
|
||||
*愤怒地挥动双臂*
|
||||
Compiling traits_example v0.1.0 (/home/hector/rust-lang-zh_CN/projects/traits_example)
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.06s
|
||||
Running `target/debug/traits_example`
|
||||
这是你们的机长在讲话。
|
||||
上升!
|
||||
*疯狂地挥舞双臂*
|
||||
```
|
||||
|
||||
由于 `fly` 方法取了一个 `self` 参数,那么当咱们有着实现了一个 *特质* 的两个 *类型* 时,Rust 就可以根据 `self` 的类型,找出要使用特质的哪个实现。
|
||||
@@ -438,9 +426,7 @@ impl fmt::Display for Point {
|
||||
随后在 `Point` 上实现 `OutlinePrint` 就将成功编译,而咱们就可以在 `Point` 实例上调用 `outline_print` 来将其实现在星号轮廓里了。
|
||||
|
||||
|
||||
## 使用新型模式在外层类型上实现外层的特质
|
||||
|
||||
**Using the Newtype Pattern to Implement External Traits on External Types**
|
||||
## 通过新型模式实现外部特质
|
||||
|
||||
|
||||
第 10 章中的 [“在类型上实现特质”](Ch10_Generic_Types_Traits_and_Lifetimes.md#在类型上实现某个特质) 小节,咱们曾提到,指明只有当特质或类型二者之一,属于代码本地的时,咱们才被允许在类型上实现特质的孤儿规则,the orphan rule。而使用涉及到在元组结构体中创建出一个新类型的 *新型模式,newtype pattern*,那么绕过这种限制便是可行的了。(咱们曾在第 5 章的 [“使用不带命名字段的元组结构体来创建不同类型”](Ch05_Using_Structs_to_Structure_Related_Data.md#使用不带命名字段的元组结构体来创建不同类型) 小节,谈到过元组结构体)这种元组结构体讲有一个字段,且将是围绕咱们要实现某个特质的类型的一个瘦封装,a thin wrapper。随后这个封装类型,便是咱们代码箱的本地类型了,而咱们就可以在这个封装上实现那个特质了。所谓 *新型,newtype*,是源自 Haskell 编程语言的一个术语。使用这种模式没有运行时性能代码,同时那个封装类型在编译时会被略去。
|
||||
|
||||
Reference in New Issue
Block a user