mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2025-04-02 23:50:24 +08:00
Update Ch15
This commit is contained in:
parent
00c734fc30
commit
482a0b1cc2
@ -209,18 +209,18 @@ fn main() {
|
||||
|
||||
**Treating Smart Pointers Like Regular References with `Deref` Trait**
|
||||
|
||||
实现 `Deref` 特质允许咱们自定义 *解引用运算符, the dereference operator*️'*' (不要与乘法或 glob 运算符相混淆)的行为。通过实现 `Deref`,智能指针可以被当作普通的引用来对待,咱们便可编写对引用进行操作的代码,并将该代码也用于智能指针。
|
||||
实现 `Deref` 特质允许咱们自定义 *解引用操作符, the dereference operator*️'*' (不要与乘法或 glob 运算符相混淆)的行为。通过实现 `Deref`,智能指针可以被当作普通的引用来对待,咱们便可编写对引用进行操作的代码,并将该代码也用于智能指针。
|
||||
|
||||
咱们首先来看看解除引用操作符是如何在常规引用中工作的。然后咱们将尝试定义一个行为类似于 `Box<T>` 的自定义类型,并看看为什么解除引用操作符在咱们新定义的类型上不像引用那样工作。咱们将探讨实现 `Deref` 特性如何使智能指针的工作方式与引用相似。然后咱们将看看 Rust 的 *解引用强制转换,deref coercion* 特性,以及其如何让咱们使用引用或智能指针工作的。
|
||||
|
||||
> 注意:咱们将要建立的 `MyBox<T>` 类型和真正的 `Box<T>` 之间有一个很大的区别:咱们的版本不会将其数据存储在堆中。咱们把这个例子的重点放在 `Deref` 上,所以数据实际存储在哪里并不重要,重要的是类似指针的行为。
|
||||
|
||||
|
||||
### 跟随指针去找值
|
||||
### 顺着指针找到值
|
||||
|
||||
**Following the Pointer to the Value**
|
||||
|
||||
常规引用即是一种指针,同时设想指针的一种方式,就好比指向存储于别处某个值的一个箭头。在下面清单 15-6 种,就创建了到某个 `i32` 值的引用,并在随后使用解引用运算符,来跟随了到该值的这个引用:
|
||||
常规引用是一种指针,而看待指针的一种方式,便是指向存储于别处值的一个箭头。在下面清单 15-6 种,咱们创建了一个对 `i32` 值的引用,然后使用解引用操作符,来跟随对该值的引用:
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@ -234,11 +234,11 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 15-6:使用解引用运算符来跟随到一个 `i32` 值的引用*
|
||||
*清单 15-6:使用解引用操作符来跟随一个 `i32` 值的引用*
|
||||
|
||||
变量 `x` 保存着一个 `i32` 值 `5`。这里将 `y` 设置为等于到 `x` 的一个引用。这里当然是可以断言,`x` 是等于 `5` 的。但是在咱们打算做出 `y` 中的断言时,这里就可以使用 `*y`,来跟随该引用到其所指向那个值(因此就叫 *解引用,dereference*),进而编译器就可以比较具体值了。一旦解引用了 `y`,咱们就有了到 `y` 所指向的那个整数的存取,那么就可以与 `5` 相比较了。
|
||||
变量 `x` 保存着一个 `i32` 值 `5`。咱们将 `y` 设置为等于到 `x` 的引用。咱们可以断言 `x` 等于 `5`。然而,如果咱们想对 `y` 中的值进行断言,咱们必须使用 `*y` 来跟随其所指向的值的音乐(因此是 *解引用,dereference*),这样编译器才能比较具体值。一旦咱们解引用了 `y`,咱们就可以访问咱们可将其与 `5` 比较的 `y` 指向的整数值。
|
||||
|
||||
相反,如果这里尝试编写 `assert_eq! (5, y);`,就会得到这样的编译报错:
|
||||
相反,如果咱们尝试编写 `assert_eq! (5, y);`,咱们便会得到下面这样的编译报错:
|
||||
|
||||
```console
|
||||
$ cargo run ✔
|
||||
@ -266,12 +266,15 @@ For more information about this error, try `rustc --explain E0277`.
|
||||
error: could not compile `sp_demos` due to previous error
|
||||
```
|
||||
|
||||
由于数字与到数字的引用属于不同类型,因此拿他们做比较是不被允许的。咱们必须使用解引用运算符,来跟随引用到其所指向的值。
|
||||
比较数字与对数字的引用是不允许的,因为他们属于不同的类型。咱们必须使用解引用操作符来跟随引用到他所指向的值。
|
||||
|
||||
|
||||
### 像引用一样使用 `Box<T>`
|
||||
|
||||
这里可将清单 15-6 中的代码,重写为使用 `Box<T>` 而非引用;清单 15-7 中在 `Box<T>` 上用到的解引用运算符,会与清单 15-6 中用在引用上的解引用运算符,以同样的方式其作用:
|
||||
**Using `Box<T>` Like a Reference**
|
||||
|
||||
|
||||
咱们可将清单 15-6 中的代码,重写为使用 `Box<T>` 而不是引用;下面清单 15-7 中 `Box<T>` 上使用的解引用操作符,与清单 15-6 中引用上使用的解引用操作符功能相同:
|
||||
|
||||
文件名:`src/main.rs`
|
||||
|
||||
@ -285,14 +288,14 @@ fn main() {
|
||||
}
|
||||
```
|
||||
|
||||
*清单 15-7:在一个 `Box<i32>` 上运用解引用运算符*
|
||||
*清单 15-7:在 `Box<i32>` 上使用解引用操作符*
|
||||
|
||||
清单 15-7 与清单 15-6 之间的主要区别,就是这里将 `y` 设置为了指向 `x` 的一个拷贝值的匣子实例,而不是清单 15-6 中那样,指向 `x` 值的引用。在最后那个断言里,咱们就可以在 `y` 为引用时,曾做过的同样方式,使用解引用运算符来跟随这个匣子的指针。接下来,将通过定义咱们自己的匣子类型,来探讨到底 `Box<T>` 有何特别之处,来实现在其上使用解引用运算符的。
|
||||
清单 15-7 与清单 15-6 之间的主要区别,就是这里将 `y` 设置为了指向 `x` 的一个拷贝值的匣子实例,而不是清单 15-6 中那样,指向 `x` 值的引用。在最后那个断言里,咱们就可以在 `y` 为引用时,曾做过的同样方式,使用解引用操作符来跟随这个匣子的指针。接下来,将通过定义咱们自己的匣子类型,来探讨到底 `Box<T>` 有何特别之处,来实现在其上使用解引用操作符的。
|
||||
|
||||
|
||||
### 定义咱们自己的灵巧指针
|
||||
|
||||
接下来就要构建出一个,类似于由标准库所提供的 `Box<T>` 类型的灵巧指针,而感受一下灵巧指针默认情况下,是怎样不同于引用的。随后就将看看,如何添加这种使用解引用运算符的能力。
|
||||
接下来就要构建出一个,类似于由标准库所提供的 `Box<T>` 类型的灵巧指针,而感受一下灵巧指针默认情况下,是怎样不同于引用的。随后就将看看,如何添加这种使用解引用操作符的能力。
|
||||
|
||||
`Box<T>` 最终被定义为有着一个元素的元组结构体(a tuple struct),因此清单 15-8 就以同样方式,定义了一个 `MyBox<T>` 类型。这里还将定义一个 `new` 函数,来与定义在 `Box<T>` 上的那个 `new` 函数相匹配。
|
||||
|
||||
@ -390,7 +393,7 @@ impl<T> Deref for MyBox<T> {
|
||||
|
||||
Rust 使用到 `deref` 方法的一个调用,以及接着一个普通的解引用,替换了那个 `*` 运算符,如此咱们就不必考虑,这里到底需不需要调用那个 `deref` 方法了。Rust 的这项特性,实现了不论对于常规引用,或是对实现了 `Deref` 特质的类型,以同样方式起作用代码的编写。
|
||||
|
||||
那个`deref` 返回到某个值的引用,以及 `*(y.deref())` 中括号外的那个普通解引用,二者都有其存在的必要原因,那就是配合了 Rust 的所有权系统。假如这个 `deref` 方法直接返回值,而不是到值的引用,那么该值就会被迁移出 `self`。咱们并未打算取得这个示例,或用到解引用运算符的其他绝大多数用例中,`MyBox<T>` 里头那个内层值的所有权。
|
||||
那个`deref` 返回到某个值的引用,以及 `*(y.deref())` 中括号外的那个普通解引用,二者都有其存在的必要原因,那就是配合了 Rust 的所有权系统。假如这个 `deref` 方法直接返回值,而不是到值的引用,那么该值就会被迁移出 `self`。咱们并未打算取得这个示例,或用到解引用操作符的其他绝大多数用例中,`MyBox<T>` 里头那个内层值的所有权。
|
||||
|
||||
请注意那个 `*` 运算符被替换为了到 `deref` 方法的一次调用,和随后到 `*` 运算符的一次调用,这替换只有一次,且咱们在代码中用到一次 `*` 运算符,这种替换就会进行一次。由于 `*` 运算符的这种替代不会无限递归,因此这里就会以类型 `i32` 的数据而结束,其正好与清单 15-9 中那个 `assert_eq!` 里的 `5` 匹配。
|
||||
|
||||
@ -1114,7 +1117,7 @@ fn main() {
|
||||
|
||||
这里创建了为 `Rc<RefCell<i32>>` 类型实例的一个值,并将其存储在名为 `value` 的一个变量中,如此咱们就可以在稍后直接访问他。接着这里在 `a` 中,创建了有着保存了 `value` 的 `Cons` 变种的一个 `List`。这里需要克隆 `value`,这样 `a` 与 `value` 都会有着那个内层值 `5` 的所有权,而非将所有权从 `value` 转移到 `a` 或让 `a` 从 `value` 借用。
|
||||
|
||||
这里把那个列表 `a`,封装在了一个 `Rc<T>` 中,进而在创建列表 `b` 与 `c` 时,二者都可以引用到 `a`,正如咱们在清单 15-18 中所做的那样。在这里已创建出 `a`、`b` 与 `c` 中的三个列表后,就打算把 `10` 加到 `value` 中的那个值。咱们是通过调用 `value` 上的 `borrow_mut` 方法做到这点的,这用到了第 5 章中曾讨论过的自动解引用特性(参见 [`->` 操作符去哪儿了?](Ch05_Using_Structs_to_Structure_Related_Data.md#--操作符the---operator哪去了呢)),来将这个 `Rc<T>` 解引用到内层的 `RefCell<T>` 值。这个 `borrow_mut` 方法返回的是一个 `RefMut<T>` 的灵巧指针,而咱们于其上使用了解引用运算符,并修改了那个内层值。
|
||||
这里把那个列表 `a`,封装在了一个 `Rc<T>` 中,进而在创建列表 `b` 与 `c` 时,二者都可以引用到 `a`,正如咱们在清单 15-18 中所做的那样。在这里已创建出 `a`、`b` 与 `c` 中的三个列表后,就打算把 `10` 加到 `value` 中的那个值。咱们是通过调用 `value` 上的 `borrow_mut` 方法做到这点的,这用到了第 5 章中曾讨论过的自动解引用特性(参见 [`->` 操作符去哪儿了?](Ch05_Using_Structs_to_Structure_Related_Data.md#--操作符the---operator哪去了呢)),来将这个 `Rc<T>` 解引用到内层的 `RefCell<T>` 值。这个 `borrow_mut` 方法返回的是一个 `RefMut<T>` 的灵巧指针,而咱们于其上使用了解引用操作符,并修改了那个内层值。
|
||||
|
||||
在打印 `a`、`b` 与 `c` 时,就可以看到他们都有了修改后的值 `15` 而非 `5`:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user