Update Ch15

This commit is contained in:
Peng Hailin, 2023-05-13 08:35:49 +08:00
parent 2939005482
commit a8b28d757d
2 changed files with 19 additions and 10 deletions

View File

@ -18,14 +18,24 @@ impl List {
}
fn main() {
let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
let a = Rc::new(
Cons(
5,
RefCell::new(Rc::new(Nil))
)
);
println! ("a 的初始 rc 计数 = {}", Rc::strong_count(&a));
println! ("a 的下一条目 = {:?}", a.tail());
let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));
let b = Rc::new(
Cons(
10,
RefCell::new(Rc::clone(&a))
)
);
println! ("b 的创建后 a 的 rc 计数 = {}", Rc::strong_count(&a));
println! ("创建 b 后 a 的 rc 计数 = {}", Rc::strong_count(&a));
println! ("b 的初始 rc 计数 = {}", Rc::strong_count(&b));
println! ("b 的下一条目 = {:?}", b.tail());

View File

@ -1067,15 +1067,14 @@ Rust 的内存安全保证,使得意外创建出从未清理过的内存(称
*清单 15-26创建出相互指向的两个 `List` 的循环引用*
我们创建一个 `Rc<List>` 实例,在变量 `a` 中持有一个 `List` 值,初始列表为 `5 Nil`。然后我们创建一个 `Rc<List>` 实例,在变量 `b` 中保存另一个 `List` 值,其中包含值 `10`,并指向 `a` 中的列表。
这里创建出了保存着变量 `a` 中,初始列表 `5, Nil` 的一个 `Rc<List>` 实例。随后这里又创建了保存着变量 `b` 中包含了值 `10` 并指向了 `a` 中清单的另一个 `Rc<List>` 实例
我们修改 `a` 使其指向 `b` 而不是 Nil从而创建一个循环。为此我们使用 `tail` 方法获取对 `a``RefCell<Rc<List>>` 的引用,我们将其放入变量 `link` 中。然后我们使用 `RefCell<Rc<List>>` 上的 `borrow_mut` 方法,将里面的值从一个持有 `Nil` 值的 `Rc<List>` 更改为 `b` 中的 `Rc<List>`
这里修改了 `a` 从而其指向了 `b` 而非 `Nil`,于是创建了一个循环。咱们是通过是要那个 `tail` 方法,来获得到 `a` 中那个 `RefCell<Rc<List>>` 的引用,这里将其放入到了变量 `link` 中。随后这里使用了这个 `RefCell<Rc<List>>` 上的 `borrow_mut` 方法,来将保存着 `Nil` 的一个 `Rc<List>`,修改为保存到 `b` 中的那个 `Rc<List>`
在保持那最后一个 `println!` 被注释掉,而运行此代码时,就会得到下面的输出:
咱们暂时保持最后的 `println!` 注释掉,而运行此代码时,咱们将得到下面的输出:
```console
$ cargo run lennyp@vm-manjaro
$ cargo run
Compiling ref_cycle_demo v0.1.0 (/home/lennyp/rust-lang/ref_cycle_demo)
Finished dev [unoptimized + debuginfo] target(s) in 1.20s
Running `target/debug/ref_cycle_demo`
@ -1088,11 +1087,11 @@ b 的下一条目 = Some(RefCell { value: Cons(5, RefCell { value: Nil }) })
在修改 a 之后 a 的 rc 计数 = 2
```
`a` 中的列表修改为指向 `b` 后,与 `b` 中的两个 `Rc<List>` 实例引用计数均为 `2`。在 `main` 的结尾Rust 会弃用掉变量 `b`,这会将那个 `b` `Rc<List>` 实例的引用计数,从 `2` 降低到 `1`。因为他的引用计数为 `1` 而不是 `0`,因此那个 `Rc<List>` 在内存堆上的内存,在此刻就不会被弃用。随后 Rust 会弃用 `a`,这同样会将那个 `a` `Rc<List>` 实例的引用计数,从 `2` 降低到 `1`。由于另一 `Rc<List>` 实例仍指向着他,因此该实例的内存也无法被弃用。那么分配给该清单的内存,将永不会被收回。为形象表示这个引用循环,这里创建出了下图 15-4 中的图示
咱们将 `a` 中的列表改为指向 `b` 后,`a` 和 `b` 中的 `Rc<List>` 实例的引用计数均为 `2`。在 `main` 的最后Rust 弃用了变量 `b`,这使得 `b` 中的 `Rc<List>` 实例的引用计数从 `2` 减少到 `1`。`Rc<List>` 在内存堆中的内存此时不会被弃用,因为其引用计数为 `1` 而不是 `0`。然后 Rust 弃用 `a`,将 `a` 中的 `Rc<List>` 实例的引用计数从 `2` 减少到 `1`。由于另一 `Rc<List>` 实例仍指向他,因此该实例的内存也不能被弃用。分配给列表内存将永远保持未被收集的状态。为直观地表示这个引用循环,咱们创建了下图 15-4 中的图表
![相互指向的列表 `a` 与 `b` 的一个循环引用](images/15-04.svg)
*图 15-04相互指向的列表 `a``b` 的一个循环引用*
*图 15-04列表 `a``b` 相互指向的引用循环*
在取消注释掉其中最后一个 `println!` 而运行该程序时Rust 就会尝试以 `a` 指向 `b` 指向 `a` 如此往复,打印出这个循环,直到他溢出内存栈为止。