|
|
@ -3,15 +3,15 @@
|
|
|
|
> [ch15-02-deref.md](https://github.com/rust-lang/book/blob/master/src/ch15-02-deref.md) > <br>
|
|
|
|
> [ch15-02-deref.md](https://github.com/rust-lang/book/blob/master/src/ch15-02-deref.md) > <br>
|
|
|
|
> commit 44f1b71c117b0dcec7805eced0b95405167092f6
|
|
|
|
> commit 44f1b71c117b0dcec7805eced0b95405167092f6
|
|
|
|
|
|
|
|
|
|
|
|
实现 `Deref` trait 允许我们重载 **解引用运算符**(_dereference operator_)`*`(与乘法运算符或 glob 运算符相区别)。通过这种方式实现 `Deref` trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
|
|
|
|
实现 `Deref` trait 允许我们重载 **解引用运算符**(_dereference operator_)`*`(与乘法运算符或通配符相区别)。通过这种方式实现 `Deref` trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
|
|
|
|
|
|
|
|
|
|
|
|
让我们首先看看解引用运算符如何处理常规引用,接着尝试定义我们自己的类似 `Box<T>` 的类型并看看为何解引用运算符不能像引用一样工作。我们会探索如何实现 `Deref` trait 使得智能指针以类似引用的方式工作变为可能。最后,我们会讨论 Rust 的 **解引用强制多态**(_deref coercions_)功能和它是如何一同处理引用或智能指针的。
|
|
|
|
让我们首先看看解引用运算符如何处理常规引用,接着尝试定义我们自己的类似 `Box<T>` 的类型并看看为何解引用运算符不能像引用一样工作。我们会探索如何实现 `Deref` trait 使得智能指针以类似引用的方式工作变为可能。最后,我们会讨论 Rust 的 **解引用强制多态**(_deref coercions_)功能以及它是如何处理引用或智能指针的。
|
|
|
|
|
|
|
|
|
|
|
|
> 我们将要构建的 `MyBox<T>` 类型与真正的 `Box<T>` 有一个巨大的区别:我们的版本不会在堆上储存数据。这个例子重点关注 `Deref`,所以其数据实际存放在何处相比其类似指针的行为来说不算重要。
|
|
|
|
> 我们将要构建的 `MyBox<T>` 类型与真正的 `Box<T>` 有一个很大的区别:我们的版本不会在堆上储存数据。这个例子重点关注 `Deref`,所以其数据实际存放在何处,相比其类似指针的行为来说不算重要。
|
|
|
|
|
|
|
|
|
|
|
|
### 通过解引用运算符追踪指针的值
|
|
|
|
### 通过解引用运算符追踪指针的值
|
|
|
|
|
|
|
|
|
|
|
|
常规引用是一个指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在示例 15-6 中,创建了一个 `i32` 值的引用接着使用解引用运算符来跟踪所引用的数据:
|
|
|
|
常规引用是一个指针类型,一种理解指针的方式是将其看成指向储存在其他某处值的箭头。在示例 15-6 中,创建了一个 `i32` 值的引用,接着使用解引用运算符来跟踪所引用的数据:
|
|
|
|
|
|
|
|
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
<span class="filename">文件名: src/main.rs</span>
|
|
|
|
|
|
|
|
|
|
|
@ -66,7 +66,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
### 自定义智能指针
|
|
|
|
### 自定义智能指针
|
|
|
|
|
|
|
|
|
|
|
|
为了体会默认智能指针的行为不同于引用,让我们创建一个类似于标准库提供的 `Box<T>` 类型的智能指针。接着会学习如何增加使用解引用运算符的功能。
|
|
|
|
为了体会默认情况下智能指针与引用的不同,让我们创建一个类似于标准库提供的 `Box<T>` 类型的智能指针。接着学习如何增加使用解引用运算符的功能。
|
|
|
|
|
|
|
|
|
|
|
|
从根本上说,`Box<T>` 被定义为包含一个元素的元组结构体,所以示例 15-8 以相同的方式定义了 `MyBox<T>` 类型。我们还定义了 `new` 函数来对应定义于 `Box<T>` 的 `new` 函数:
|
|
|
|
从根本上说,`Box<T>` 被定义为包含一个元素的元组结构体,所以示例 15-8 以相同的方式定义了 `MyBox<T>` 类型。我们还定义了 `new` 函数来对应定义于 `Box<T>` 的 `new` 函数:
|
|
|
|
|
|
|
|
|
|
|
@ -112,7 +112,7 @@ error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
|
|
|
|
| ^^
|
|
|
|
| ^^
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`MyBox<T>` 类型不能解引用我们并没有为其实现这个功能。为了启用 `*` 运算符的解引用功能,需要实现 `Deref` trait。
|
|
|
|
`MyBox<T>` 类型不能解引用,因为我们尚未在该类型实现这个功能。为了启用 `*` 运算符的解引用功能,需要实现 `Deref` trait。
|
|
|
|
|
|
|
|
|
|
|
|
### 通过实现 `Deref` trait 将某类型像引用一样处理
|
|
|
|
### 通过实现 `Deref` trait 将某类型像引用一样处理
|
|
|
|
|
|
|
|
|
|
|
@ -124,6 +124,7 @@ error[E0614]: type `MyBox<{integer}>` cannot be dereferenced
|
|
|
|
use std::ops::Deref;
|
|
|
|
use std::ops::Deref;
|
|
|
|
|
|
|
|
|
|
|
|
# struct MyBox<T>(T);
|
|
|
|
# struct MyBox<T>(T);
|
|
|
|
|
|
|
|
|
|
|
|
impl<T> Deref for MyBox<T> {
|
|
|
|
impl<T> Deref for MyBox<T> {
|
|
|
|
type Target = T;
|
|
|
|
type Target = T;
|
|
|
|
|
|
|
|
|
|
|
@ -139,7 +140,7 @@ impl<T> Deref for MyBox<T> {
|
|
|
|
|
|
|
|
|
|
|
|
`deref` 方法体中写入了 `&self.0`,这样 `deref` 返回了我希望通过 `*` 运算符访问的值的引用。示例 15-9 中的 `main` 函数中对 `MyBox<T>` 值的 `*` 调用现在可以编译并能通过断言了!
|
|
|
|
`deref` 方法体中写入了 `&self.0`,这样 `deref` 返回了我希望通过 `*` 运算符访问的值的引用。示例 15-9 中的 `main` 函数中对 `MyBox<T>` 值的 `*` 调用现在可以编译并能通过断言了!
|
|
|
|
|
|
|
|
|
|
|
|
没有 `Deref` trait 的话,编译器只会解引用 `&` 引用类型。`deref` 方法向编译器提供了获取任何实现了 `Deref` trait 的类型的值并调用这个类型的 `deref` 方法来获取一个它知道如何解引用的 `&` 引用的能力。
|
|
|
|
没有 `Deref` trait 的话,编译器只会解引用 `&` 引用类型。`deref` 方法向编译器提供了获取任何实现了 `Deref` trait 的类型的值,并且调用这个类型的 `deref` 方法来获取一个它知道如何解引用的 `&` 引用的能力。
|
|
|
|
|
|
|
|
|
|
|
|
当我们在示例 15-9 中输入 `*y` 时,Rust 事实上在底层运行了如下代码:
|
|
|
|
当我们在示例 15-9 中输入 `*y` 时,Rust 事实上在底层运行了如下代码:
|
|
|
|
|
|
|
|
|
|
|
@ -147,7 +148,7 @@ impl<T> Deref for MyBox<T> {
|
|
|
|
*(y.deref())
|
|
|
|
*(y.deref())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Rust 将 `*` 运算符替换为先调用 `deref` 方法再进行直接引用的操作,如此我们便不用担心是不是还需要手动调用 `deref` 方法了。Rust 的这个特性可以让我们写出行为一致的代码,无论是面对的是常规引用还是实现了 `Deref` 的类型。
|
|
|
|
Rust 将 `*` 运算符替换为先调用 `deref` 方法再进行普通解引用的操作,如此我们便不用担心是否还需手动调用 `deref` 方法了。Rust 的这个特性可以让我们写出行为一致的代码,无论是面对的是常规引用还是实现了 `Deref` 的类型。
|
|
|
|
|
|
|
|
|
|
|
|
`deref` 方法返回值的引用,以及 `*(y.deref())` 括号外边的普通解引用仍为必须的原因在于所有权。如果 `deref` 方法直接返回值而不是值的引用,其值(的所有权)将被移出 `self`。在这里以及大部分使用解引用运算符的情况下我们并不希望获取 `MyBox<T>` 内部值的所有权。
|
|
|
|
`deref` 方法返回值的引用,以及 `*(y.deref())` 括号外边的普通解引用仍为必须的原因在于所有权。如果 `deref` 方法直接返回值而不是值的引用,其值(的所有权)将被移出 `self`。在这里以及大部分使用解引用运算符的情况下我们并不希望获取 `MyBox<T>` 内部值的所有权。
|
|
|
|
|
|
|
|
|
|
|
@ -155,7 +156,7 @@ Rust 将 `*` 运算符替换为先调用 `deref` 方法再进行直接引用的
|
|
|
|
|
|
|
|
|
|
|
|
### 函数和方法的隐式解引用强制多态
|
|
|
|
### 函数和方法的隐式解引用强制多态
|
|
|
|
|
|
|
|
|
|
|
|
**解引用强制多态**(_deref coercions_)是 Rust 表现在函数或方法传参上的一种便利。其将实现了 `Deref` 的类型的引用转换为原始类型通过 `Deref` 所能够转换的类型的引用。当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时,解引用强制多态将自动发生。这时会有一系列的 `deref` 方法被调用,把我们提供的类型转换成了参数所需的类型。
|
|
|
|
**解引用强制多态**(_deref coercions_)是 Rust 在函数或方法传参上的一种便利。其将实现了 `Deref` 的类型的引用转换为原始类型通过 `Deref` 所能够转换的类型的引用。当这种特定类型的引用作为实参传递给和形参类型不同的函数或方法时,解引用强制多态将自动发生。这时会有一系列的 `deref` 方法被调用,把我们提供的类型转换成了参数所需的类型。
|
|
|
|
|
|
|
|
|
|
|
|
解引用强制多态的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用 `&` 和 `*` 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。
|
|
|
|
解引用强制多态的加入使得 Rust 程序员编写函数和方法调用时无需增加过多显式使用 `&` 和 `*` 的引用和解引用。这个功能也使得我们可以编写更多同时作用于引用或智能指针的代码。
|
|
|
|
|
|
|
|
|
|
|
|