补全 “Rc

This commit is contained in:
wtklbm 2020-06-27 08:58:45 +08:00
parent 53f8710e5e
commit c3cccf1ba7
2 changed files with 6 additions and 6 deletions

View File

@ -65,7 +65,7 @@ error[E0382]: use of moved value: `a`
可以改变 `Cons` 的定义来存放一个引用,不过接着必须指定生命周期参数。通过指定生命周期参数,表明列表中的每一个元素都至少与列表本身存在的一样久。例如,借用检查器不会允许 `let a = Cons(10, &Nil);` 编译,因为临时值 `Nil` 会在 `a` 获取其引用之前就被丢弃了。
相反,我们修改 `List` 的定义为使用 `Rc<T>` 代替 `Box<T>`,如列表 15-18 所示。现在每一个 `Cons` 变量都包含一个值和一个指向 `List``Rc`。当创建 `b` 时,不同于获取 `a` 的所有权,这里会克隆 `a` 所包含的 `Rc`,这会将引用计数从 1 增加到 2 并允许 `a``b` 共享 `Rc` 中数据的所有权。创建 `c` 时也会克隆 `a`,这会将引用计数从 2 增加为 3。每次调用 `Rc::clone``Rc` 中数据的引用计数都会增加,直到有零个引用之前其数据都不会被清理。
相反,我们修改 `List` 的定义为使用 `Rc<T>` 代替 `Box<T>`,如列表 15-18 所示。现在每一个 `Cons` 变量都包含一个值和一个指向 `List``Rc<T>`。当创建 `b` 时,不同于获取 `a` 的所有权,这里会克隆 `a` 所包含的 `Rc<List>`,这会将引用计数从 1 增加到 2 并允许 `a``b` 共享 `Rcc<List>` 中数据的所有权。创建 `c` 时也会克隆 `a`,这会将引用计数从 2 增加为 3。每次调用 `Rc::clone``Rc<List>` 中数据的引用计数都会增加,直到有零个引用之前其数据都不会被清理。
<span class="filename">文件名: src/main.rs</span>
@ -136,7 +136,7 @@ count after c goes out of scope = 2
我们能够看到 `a``Rc<List>` 的初始引用计数为1接着每次调用 `clone`计数会增加1。当 `c` 离开作用域时计数减1。不必像调用 `Rc::clone` 增加引用计数那样调用一个函数来减少计数;`Drop` trait 的实现当 `Rc<T>` 值离开作用域时自动减少引用计数。
从这个例子我们所不能看到的是,在 `main` 的结尾当 `b` 然后是 `a` 离开作用域时,此处计数会是 0同时 `Rc` 被完全清理。使用 `Rc` 允许一个值有多个所有者,引用计数则确保只要任何所有者依然存在其值也保持有效。
从这个例子我们所不能看到的是,在 `main` 的结尾当 `b` 然后是 `a` 离开作用域时,此处计数会是 0同时 `Rc<List>` 被完全清理。使用 `Rc<T>` 允许一个值有多个所有者,引用计数则确保只要任何所有者依然存在其值也保持有效。
通过不可变引用, `Rc<T>` 允许在程序的多个部分之间只读地共享数据。如果 `Rc<T>` 也允许多个可变引用,则会违反第四章讨论的借用规则之一:相同位置的多个可变借用可能造成数据竞争和不一致。不过可以修改数据是非常有用的!在下一部分,我们将讨论内部可变性模式和 `RefCell<T>` 类型,它可以与 `Rc<T>` 结合使用来处理不可变性的限制。

View File

@ -89,7 +89,7 @@ fn main() {
这里在变量 `a` 中创建了一个 `Rc<List>` 实例来存放初值为 `5, Nil``List` 值。接着在变量 `b` 中创建了存放包含值 10 和指向列表 `a``List` 的另一个 `Rc<List>` 实例。
最后,修改 `a` 使其指向 `b` 而不是 `Nil`,这就创建了一个循环。为此需要使用 `tail` 方法获取 `a``RefCell<Rc<List>>` 的引用,并放入变量 `link` 中。接着使用 `RefCell<Rc<List>>``borrow_mut` 方法将其值从存放 `Nil``Rc` 修改为 `b` 中的 `Rc<List>`
最后,修改 `a` 使其指向 `b` 而不是 `Nil`,这就创建了一个循环。为此需要使用 `tail` 方法获取 `a``RefCell<Rc<List>>` 的引用,并放入变量 `link` 中。接着使用 `RefCell<Rc<List>>``borrow_mut` 方法将其值从存放 `Nil``Rc<List>` 修改为 `b` 中的 `Rc<List>`
如果保持最后的 `println!` 行注释并运行代码,会得到如下输出:
@ -103,7 +103,7 @@ b rc count after changing a = 2
a rc count after changing a = 2
```
可以看到将 `a` 修改为指向 `b` 之后,`a` 和 `b` 中都有的 `Rc<List>` 实例的引用计数为 2。在 `main` 的结尾Rust 会尝试首先丢弃 `b`,这会使 `a``b``Rc` 实例的引用计数减 1。
可以看到将 `a` 修改为指向 `b` 之后,`a` 和 `b` 中都有的 `Rc<List>` 实例的引用计数为 2。在 `main` 的结尾Rust 会尝试首先丢弃 `b`,这会使 `a``b``Rc<List>` 实例的引用计数减 1。
然而,因为 `a` 仍然引用 `b` 中的 `Rc<List>``Rc<List>` 的引用计数是 1 而不是 0所以 `Rc<List>` 在堆上的内存不会被丢弃。其内存会因为引用计数为 1 而永远停留。为了更形象的展示,我们创建了一个如图 15-4 所示的引用循环:
@ -121,7 +121,7 @@ a rc count after changing a = 2
### 避免引用循环:将 `Rc<T>` 变为 `Weak<T>`
到目前为止,我们已经展示了调用 `Rc::clone` 会增加 `Rc<T>` 实例的 `strong_count`,和只在其 `strong_count` 为 0 时才会被清理的 `Rc<T>` 实例。你也可以通过调用 `Rc::downgrade` 并传递 `Rc` 实例的引用来创建其值的 **弱引用**_weak reference_。调用 `Rc::downgrade` 时会得到 `Weak<T>` 类型的智能指针。不同于将 `Rc<T>` 实例的 `strong_count` 加1调用 `Rc::downgrade` 会将 `weak_count` 加1。`Rc<T>` 类型使用 `weak_count` 来记录其存在多少个 `Weak<T>` 引用,类似于 `strong_count`。其区别在于 `weak_count` 无需计数为 0 就能使 `Rc` 实例被清理。
到目前为止,我们已经展示了调用 `Rc::clone` 会增加 `Rc<T>` 实例的 `strong_count`,和只在其 `strong_count` 为 0 时才会被清理的 `Rc<T>` 实例。你也可以通过调用 `Rc::downgrade` 并传递 `Rc<T>` 实例的引用来创建其值的 **弱引用**_weak reference_。调用 `Rc::downgrade` 时会得到 `Weak<T>` 类型的智能指针。不同于将 `Rc<T>` 实例的 `strong_count` 加1调用 `Rc::downgrade` 会将 `weak_count` 加1。`Rc<T>` 类型使用 `weak_count` 来记录其存在多少个 `Weak<T>` 引用,类似于 `strong_count`。其区别在于 `weak_count` 无需计数为 0 就能使 `Rc<T>` 实例被清理。
强引用代表如何共享 `Rc<T>` 实例的所有权,但弱引用并不属于所有权关系。他们不会造成引用循环,因为任何弱引用的循环会在其相关的强引用计数为 0 时被打断。
@ -247,7 +247,7 @@ fn main() {
leaf parent = None
```
当创建 `branch` 节点时,其也会新建一个 `Weak<Node>` 引用,因为 `branch` 并没有父节点。`leaf` 仍然作为 `branch` 的一个子节点。一旦在 `branch` 中有了 `Node` 实例,就可以修改 `leaf` 使其拥有指向父节点的 `Weak<Node>` 引用。这里使用了 `leaf``parent` 字段里的 `RefCell<Weak<Node>>``borrow_mut` 方法,接着使用了 `Rc::downgrade` 函数来从 `branch` 中的 `Rc` 值创建了一个指向 `branch``Weak<Node>` 引用。
当创建 `branch` 节点时,其也会新建一个 `Weak<Node>` 引用,因为 `branch` 并没有父节点。`leaf` 仍然作为 `branch` 的一个子节点。一旦在 `branch` 中有了 `Node` 实例,就可以修改 `leaf` 使其拥有指向父节点的 `Weak<Node>` 引用。这里使用了 `leaf``parent` 字段里的 `RefCell<Weak<Node>>``borrow_mut` 方法,接着使用了 `Rc::downgrade` 函数来从 `branch` 中的 `Rc<Node>` 值创建了一个指向 `branch``Weak<Node>` 引用。
当再次打印出 `leaf` 的父节点时,这一次将会得到存放了 `branch``Some` 值:现在 `leaf` 可以访问其父节点了!当打印出 `leaf` 时,我们也避免了如示例 15-26 中最终会导致栈溢出的循环:`Weak<Node>` 引用被打印为 `(Weak)`