mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2025-04-26 16:08:03 +08:00
update ch17-05
This commit is contained in:
parent
015e5137f6
commit
50a358a2ae
@ -134,6 +134,51 @@ pub trait Future {
|
||||
|
||||
回忆一下 `await` 的实现是基于对 `poll` 的调用,这有助于解释之前见到的错误信息,不过那是关于 `Unpin` 的。所以 `Pin` 具体与 `Unpin` 有何关联,又为什么 `Future` 需要 `self` 在一个 `Pin` 类型中才能调用 `poll`?
|
||||
|
||||
回忆一下本章之前 future 中一系列的 await point 被编译为一个状态机,而编译器确保这个状态机遵守所有 Rust 在安全方面的正常规则,包括借用和所有权。为了使其正常工作,Rust 检查在一个 await point 和另一个 await point 之间或到异步代码块结尾之间什么数据是需要的。它接着在编译的状态机中创建一个相应的变体。每一个变体获得其在源码中对应片段所用到的数据的访问权限,要么获取数据的所有权要么获取其可变或不可变引用。
|
||||
|
||||
到目前为止, 一切正常:如果你在给定异步代码块中搞错了所有权或者引用,借用检查器会告诉你。当我们需要移动对应代码块的 future -- 例如将其移动到 `Vec` 中或者传递给 `join_all` -- 事情就会变得棘手。
|
||||
|
||||
当我们移动一个 future -- 将其移动进一个数据结构通过 `join_all` 将其用作迭代器或者将其从函数返回 -- 这实际上意味着将 Rust 创建的状态机移动给我们。同时不同于 Rust 中大部分其它类型,Rust 为异步代码块创建的 future 可能最终会在任意给定变体的字段中有其自身的引用,如图 17-4 中的简化图所示。
|
||||
|
||||
<figure>
|
||||
|
||||
<img alt="A single-column, three-row table representing a future, fut1, which has data values 0 and 1 in the first two rows and an arrow pointing from the third row back to the second row, representing an internal reference within the future." src="img/trpl17-04.svg" class="center" />
|
||||
|
||||
<figcaption>图 17-4: 一个自引用数据类型。</figcaption>
|
||||
|
||||
</figure>
|
||||
|
||||
但是,默认移动任何拥有其自身引用的对象是不安全(unsafe)的,因为引用总是会指向任何其引用数据的实际内存地址(见图 17-5)。如果我们移动数据结构本身,这些内部引用会停留在指向老的地址。然后,这些内存地址现在是无效的。一方面,当修改这些数据结构时这些值不会被更新。另一个更重要的方面是,电脑现在可以随意复用这些内存用于其它用途!之后你最终可能会读取到完全不相关的数据。
|
||||
|
||||
<figure>
|
||||
|
||||
<img alt="Two tables, depicting two futures, fut1 and fut2, each of which has one column and three rows, representing the result of having moved a future out of fut1 into fut2. The first, fut1, is grayed out, with a question mark in each index, representing unknown memory. The second, fut2, has 0 and 1 in the first and second rows and an arrow pointing from its third row back to the second row of fut1, representing a pointer that is referencing the old location in memory of the future before it was moved." src="img/trpl17-05.svg" class="center" />
|
||||
|
||||
<figcaption>图 17-5: 移动一个自引用数据类型的不安全结果。</figcaption>
|
||||
|
||||
</figure>
|
||||
|
||||
理论上来说,Rust 编译器也可以在对象被移动时尝试更新其所有的引用,不过这会增加很多性能开销,特别是在有一整个网状的引用需要更新的时候。相反如果我们确保相关的数据结构**不能再内存中移动**,我们就无需更新任何引用。这正是 Rust 的借用检查器所要求的:在安全代码中,禁止移动任何有自身活动引用的项。
|
||||
|
||||
构建于这之上的 `Pin` 正好给了我们所需的保证。当我们通过 `Pin` 封装一个值的引用来 **pin** 住它时,它就不能再移动了。也就是说,如果有 `Pin<Box<SomeType>>`,你实际上 ping 住了 `SomeType` 的值,而**不是** `Box` 指针。图 17-6 解释了这一过程。
|
||||
|
||||
<figure>
|
||||
|
||||
<img alt="Three boxes laid out side by side. The first is labeled “Pin”, the second “b1”, and the third “pinned”. Within “pinned” is a table labeled “fut”, with a single column; it represents a future with cells for each part of the data structure. Its first cell has the value “0”, its second cell has an arrow coming out of it and pointing to the fourth and final cell, which has the value “1” in it, and the third cell has dashed lines and an ellipsis to indicate there may be other parts to the data structure. All together, the “fut” table represents a future which is self-referential. An arrow leaves the box labeled “Pin”, goes through the box labeled “b1” and has terminates inside the “pinned” box at the “fut” table." src="img/trpl17-06.svg" class="center" />
|
||||
|
||||
<figcaption>图 17-6: pin 住一个指向自引用 future 类型的 `Box`。</figcaption>
|
||||
|
||||
</figure>
|
||||
|
||||
事实上,`Box` 指针仍然可以随意移动。请记住:我们关心确保最终被引用的数据保持不动。如果指针移动了,**但是它指向的数据还在相同的位置**,就像图 17-7 一样,就不会有潜在的问题。(作为一个独立的练习,查看一下 `Pin` 类型以及 `std::pin` 模块的文档来尝试理解如何通过 `Pin` 封装一个 `Box` 来做到这些。)其关键在于自引用类型本身不可移动,因为它仍然是被 pin 住的。
|
||||
|
||||
<figure>
|
||||
|
||||
<img alt="Four boxes laid out in three rough columns, identical to the previous diagram with a change to the second column. Now there are two boxes in the second column, labeled “b1” and “b2”, “b1” is grayed out, and the arrow from “Pin” goes through “b2” instead of “b1”, indicating that the pointer has moved from “b1” to “b2”, but the data in “pinned” has not moved." src="img/trpl17-07.svg" class="center" />
|
||||
|
||||
<figcaption>图 17-7: 移动一个指向自引用 future 类型的 `Box`。</figcaption>
|
||||
|
||||
</figure>
|
||||
|
||||
[ch-18]: ch18-00-oop.html
|
||||
[async-book]: https://rust-lang.github.io/async-book/
|
||||
|
Loading…
Reference in New Issue
Block a user