|
|
|
@ -5,7 +5,7 @@
|
|
|
|
|
|
|
|
|
|
实现 `Deref` trait 允许我们重载 **解引用运算符**(_dereference operator_)`*`(与乘法运算符或通配符相区别)。通过这种方式实现 `Deref` trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
|
|
|
|
|
|
|
|
|
|
让我们首先看看解引用运算符如何处理常规引用,接着尝试定义我们自己的类似 `Box<T>` 的类型并看看为何解引用运算符不能像引用一样工作。我们会探索如何实现 `Deref` trait 使得智能指针以类似引用的方式工作变为可能。最后,我们会讨论 Rust 的 **解引用强制多态**(_deref coercions_)功能以及它是如何处理引用或智能指针的。
|
|
|
|
|
让我们首先看看解引用运算符如何处理常规引用,接着尝试定义我们自己的类似 `Box<T>` 的类型并看看为何解引用运算符不能像引用一样工作。我们会探索如何实现 `Deref` trait 使得智能指针以类似引用的方式工作变为可能。最后,我们会讨论 Rust 的 **Deref 强制转换**(_deref coercions_)功能以及它是如何处理引用或智能指针的。
|
|
|
|
|
|
|
|
|
|
> 我们将要构建的 `MyBox<T>` 类型与真正的 `Box<T>` 有一个很大的区别:我们的版本不会在堆上储存数据。这个例子重点关注 `Deref`,所以其数据实际存放在何处,相比其类似指针的行为来说不算重要。
|
|
|
|
|
|
|
|
|
@ -154,13 +154,13 @@ Rust 将 `*` 运算符替换为先调用 `deref` 方法再进行普通解引用
|
|
|
|
|
|
|
|
|
|
注意,每次当我们在代码中使用 `*` 时, `*` 运算符都被替换成了先调用 `deref` 方法再接着使用 `*` 解引用的操作,且只会发生一次,不会对 `*` 操作符无限递归替换,解引用出上面 `i32` 类型的值就停止了,这个值与示例 15-9 中 `assert_eq!` 的 `5` 相匹配。
|
|
|
|
|
|
|
|
|
|
### 函数和方法的隐式解引用强制多态
|
|
|
|
|
### 函数和方法的隐式 Deref 强制转换
|
|
|
|
|
|
|
|
|
|
**解引用强制多态**(_deref coercions_)是 Rust 在函数或方法传参上的一种便利。其将实现了 `Deref` 的类型的引用转换为原始类型通过 `Deref` 所能够转换的类型的引用。当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时,解引用强制多态将自动发生。这时会有一系列的 `deref` 方法被调用,把我们提供的类型转换成了参数所需的类型。
|
|
|
|
|
**Deref 强制转换**(_deref coercions_)是 Rust 在函数或方法传参上的一种便利。其将实现了 `Deref` 的类型的引用转换为原始类型通过 `Deref` 所能够转换的类型的引用。当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时,Deref 强制转换将自动发生。这时会有一系列的 `deref` 方法被调用,把我们提供的类型转换成了参数所需的类型。
|
|
|
|
|
|
|
|
|
|
解引用强制多态的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用 `&` 和 `*` 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。
|
|
|
|
|
Deref 强制转换的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用 `&` 和 `*` 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。
|
|
|
|
|
|
|
|
|
|
作为展示解引用强制多态的实例,让我们使用示例 15-8 中定义的 `MyBox<T>`,以及示例 15-10 中增加的 `Deref` 实现。示例 15-11 展示了一个有着字符串 slice 参数的函数定义:
|
|
|
|
|
作为展示 Deref 强制转换的实例,让我们使用示例 15-8 中定义的 `MyBox<T>`,以及示例 15-10 中增加的 `Deref` 实现。示例 15-11 展示了一个有着字符串 slice 参数的函数定义:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
@ -172,7 +172,7 @@ fn hello(name: &str) {
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 15-11:`hello` 函数有着 `&str` 类型的参数 `name`</span>
|
|
|
|
|
|
|
|
|
|
可以使用字符串 slice 作为参数调用 `hello` 函数,比如 `hello("Rust");`。解引用强制多态使得用 `MyBox<String>` 类型值的引用调用 `hello` 成为可能,如示例 15-12 所示:
|
|
|
|
|
可以使用字符串 slice 作为参数调用 `hello` 函数,比如 `hello("Rust");`。Deref 强制转换使得用 `MyBox<String>` 类型值的引用调用 `hello` 成为可能,如示例 15-12 所示:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
@ -205,11 +205,11 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 15-12:因为解引用强制多态,使用 `MyBox<String>` 的引用调用 `hello` 是可行的</span>
|
|
|
|
|
<span class="caption">示例 15-12:因为 Deref 强制转换,使用 `MyBox<String>` 的引用调用 `hello` 是可行的</span>
|
|
|
|
|
|
|
|
|
|
这里使用 `&m` 调用 `hello` 函数,其为 `MyBox<String>` 值的引用。因为示例 15-10 中在 `MyBox<T>` 上实现了 `Deref` trait,Rust 可以通过 `deref` 调用将 `&MyBox<String>` 变为 `&String`。标准库中提供了 `String` 上的 `Deref` 实现,其会返回字符串 slice,这可以在 `Deref` 的 API 文档中看到。Rust 再次调用 `deref` 将 `&String` 变为 `&str`,这就符合 `hello` 函数的定义了。
|
|
|
|
|
|
|
|
|
|
如果 Rust 没有实现解引用强制多态,为了使用 `&MyBox<String>` 类型的值调用 `hello`,则不得不编写示例 15-13 中的代码来代替示例 15-12:
|
|
|
|
|
如果 Rust 没有实现 Deref 强制转换,为了使用 `&MyBox<String>` 类型的值调用 `hello`,则不得不编写示例 15-13 中的代码来代替示例 15-12:
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
@ -242,17 +242,17 @@ fn main() {
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
<span class="caption">示例 15-13:如果 Rust 没有解引用强制多态则必须编写的代码</span>
|
|
|
|
|
<span class="caption">示例 15-13:如果 Rust 没有 Deref 强制转换则必须编写的代码</span>
|
|
|
|
|
|
|
|
|
|
`(*m)` 将 `MyBox<String>` 解引用为 `String`。接着 `&` 和 `[..]` 获取了整个 `String` 的字符串 slice 来匹配 `hello` 的签名。没有解引用强制多态所有这些符号混在一起将更难以读写和理解。解引用强制多态使得 Rust 自动的帮我们处理这些转换。
|
|
|
|
|
`(*m)` 将 `MyBox<String>` 解引用为 `String`。接着 `&` 和 `[..]` 获取了整个 `String` 的字符串 slice 来匹配 `hello` 的签名。没有 Deref 强制转换所有这些符号混在一起将更难以读写和理解。Deref 强制转换使得 Rust 自动的帮我们处理这些转换。
|
|
|
|
|
|
|
|
|
|
当所涉及到的类型定义了 `Deref` trait,Rust 会分析这些类型并使用任意多次 `Deref::deref` 调用以获得匹配参数的类型。这些解析都发生在编译时,所以利用解引用强制多态并没有运行时惩罚!
|
|
|
|
|
当所涉及到的类型定义了 `Deref` trait,Rust 会分析这些类型并使用任意多次 `Deref::deref` 调用以获得匹配参数的类型。这些解析都发生在编译时,所以利用 Deref 强制转换并没有运行时惩罚!
|
|
|
|
|
|
|
|
|
|
### 解引用强制多态如何与可变性交互
|
|
|
|
|
### Deref 强制转换如何与可变性交互
|
|
|
|
|
|
|
|
|
|
类似于如何使用 `Deref` trait 重载不可变引用的 `*` 运算符,Rust 提供了 `DerefMut` trait 用于重载可变引用的 `*` 运算符。
|
|
|
|
|
|
|
|
|
|
Rust 在发现类型和 trait 实现满足三种情况时会进行解引用强制多态:
|
|
|
|
|
Rust 在发现类型和 trait 实现满足三种情况时会进行 Deref 强制转换:
|
|
|
|
|
|
|
|
|
|
- 当 `T: Deref<Target=U>` 时从 `&T` 到 `&U`。
|
|
|
|
|
- 当 `T: DerefMut<Target=U>` 时从 `&mut T` 到 `&mut U`。
|
|
|
|
|