diff --git a/docs/ch15-05-interior-mutability.html b/docs/ch15-05-interior-mutability.html
index 226197c..a3bdf7a 100644
--- a/docs/ch15-05-interior-mutability.html
+++ b/docs/ch15-05-interior-mutability.html
@@ -73,7 +73,7 @@
commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
-内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用unsafe
代码来绕过 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的unsafe
代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
+内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用unsafe
代码来模糊 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的unsafe
代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
让我们通过遵循内部可变性模式的RefCell<T>
类型来开始探索。
不同于Rc<T>
,RefCell<T>
代表其数据的唯一的所有权。那么是什么让RefCell<T>
不同于像Box<T>
这样的类型呢?回忆一下第四章所学的借用规则:
@@ -92,6 +92,113 @@ commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
因为一些分析是不可能的,Rust 编译器在其不确定的时候甚至都不尝试猜测,所以说它是保守的而且有时会拒绝事实上不会违反 Rust 保证的正确的程序。换句话说,如果 Rust 接受不正确的程序,那么人们也就不会相信 Rust 所做的保证了。如果 Rust 拒绝正确的程序,会给程序员带来不变,但不会带来灾难。RefCell<T>
正是用于当你知道代码遵守借用规则,而编译器不能理解的时候。
类似于Rc<T>
,RefCell<T>
只能用于单线程场景。在并发章节会介绍如何在多线程程序中使用RefCell<T>
的功能。现在所有你需要知道的就是如果尝试在多线程上下文中使用RefCell<T>
,会得到一个编译错误。
对于引用,可以使用&
和&mut
语法来分别创建不可变和可变的引用。不过对于RefCell<T>
,我们使用borrow
和borrow_mut
方法,它是RefCell<T>
拥有的安全 API 的一部分。borrow
返回Ref
类型的智能指针,而borrow_mut
返回RefMut
类型的智能指针。这两个类型实现了Deref
所以可以被当作常规引用处理。Ref
和RefMut
动态的借用所有权,而他们的Drop
实现也动态的释放借用。
+列表 15-14 展示了如何使用RefCell<T>
来使函数不可变的和可变的借用它的参数。注意data
变量使用let data
而不是let mut data
来声明为不可变的,而a_fn_that_mutably_borrows
则允许可变的借用数据并修改它!
+Filename: src/main.rs
+use std::cell::RefCell;
+
+fn a_fn_that_immutably_borrows(a: &i32) {
+ println!("a is {}", a);
+}
+
+fn a_fn_that_mutably_borrows(b: &mut i32) {
+ *b += 1;
+}
+
+fn demo(r: &RefCell<i32>) {
+ a_fn_that_immutably_borrows(&r.borrow());
+ a_fn_that_mutably_borrows(&mut r.borrow_mut());
+ a_fn_that_immutably_borrows(&r.borrow());
+}
+
+fn main() {
+ let data = RefCell::new(5);
+ demo(&data);
+}
+
+Listing 15-14: Using RefCell<T>
, borrow
, and
+borrow_mut
+这个例子打印出:
+a is 5
+a is 6
+
+在main
函数中,我们新声明了一个包含值 5 的RefCell<T>
,并储存在变量data
中,声明时并没有使用mut
关键字。接着使用data
的一个不可变引用来调用demo
函数:对于main
函数而言data
是不可变的!
+在demo
函数中,通过调用borrow
方法来获取到RefCell<T>
中值的不可变引用,并使用这个不可变引用调用了a_fn_that_immutably_borrows
函数。更为有趣的是,可以通过borrow_mut
方法来获取RefCell<T>
中值的可变引用,而a_fn_that_mutably_borrows
函数就允许修改这个值。可以看到下一次调用a_fn_that_immutably_borrows
时打印出的值是 6 而不是 5。
+
+回忆一下第四章因为借用规则,尝试使用常规引用在同一作用域中创建两个可变引用的代码无法编译:
+let mut s = String::from("hello");
+
+let r1 = &mut s;
+let r2 = &mut s;
+
+这会得到一个编译错误:
+error[E0499]: cannot borrow `s` as mutable more than once at a time
+ -->
+ |
+5 | let r1 = &mut s;
+ | - first mutable borrow occurs here
+6 | let r2 = &mut s;
+ | ^ second mutable borrow occurs here
+7 | }
+ | - first borrow ends here
+
+与此相反,使用RefCell<T>
并在同一作用域调用两次borrow_mut
的代码是可以编译的,不过它会在运行时 panic。如下代码:
+use std::cell::RefCell;
+
+fn main() {
+ let s = RefCell::new(String::from("hello"));
+
+ let r1 = s.borrow_mut();
+ let r2 = s.borrow_mut();
+}
+
+能够编译不过在cargo run
运行时会出现如下错误:
+ Finished dev [unoptimized + debuginfo] target(s) in 0.83 secs
+ Running `target/debug/refcell`
+thread 'main' panicked at 'already borrowed: BorrowMutError',
+/stable-dist-rustc/build/src/libcore/result.rs:868
+note: Run with `RUST_BACKTRACE=1` for a backtrace.
+
+这个运行时BorrowMutError
类似于编译错误:它表明已经可变的借用过s
一次了,所以不允许再次借用它。我们并没有绕过借用规则,只是选择让 Rust 在运行时而不是编译时执行他们。你可以选择在任何时候任何地方使用RefCell<T>
,不过除了不得不编写很多RefCell
之外,最终还是可能会发现其中的问题(可能是在生产环境而不是开发环境)。另外,在运行时检查借用规则有性能惩罚。
+
+那么为什么要权衡考虑选择引入RefCell<T>
呢?好吧,还记得我们说过Rc<T>
只能拥有一个T
的不可变引用吗?考虑到RefCell<T>
是不可变的,但是拥有内部可变性,可以将Rc<T>
与RefCell<T>
结合来创造一个既有引用计数又可变的类型。列表 15-15 展示了一个这么做的例子,再次回到列表 15-5 中的 cons list。在这个例子中,不同于在 cons list 中储存i32
值,我们储存一个Rc<RefCell<i32>>
值。希望储存这个类型是因为其可以拥有不属于列表一部分的这个值的所有者(Rc<T>
提供的多个所有者功能),而且还可以改变内部的i32
值(RefCell<T>
提供的内部可变性功能):
+Filename: src/main.rs
+#[derive(Debug)]
+enum List {
+ Cons(Rc<RefCell<i32>>, Rc<List>),
+ Nil,
+}
+
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+ let value = Rc::new(RefCell::new(5));
+
+ let a = Cons(value.clone(), Rc::new(Nil));
+ let shared_list = Rc::new(a);
+
+ let b = Cons(Rc::new(RefCell::new(6)), shared_list.clone());
+ let c = Cons(Rc::new(RefCell::new(10)), shared_list.clone());
+
+ *value.borrow_mut() += 10;
+
+ println!("shared_list after = {:?}", shared_list);
+ println!("b after = {:?}", b);
+ println!("c after = {:?}", c);
+}
+
+Listing 15-15: Using Rc<RefCell<i32>>
to create a
+List
that we can mutate
+我们创建了一个值,它是Rc<RefCell<i32>>
的实例。将其储存在变量value
中因为我们希望之后能直接访问它。接着在a
中创建了一个拥有存放了value
值的Cons
成员的List
,而且value
需要被克隆因为我们希望除了a
之外还拥有value
的所有权。接着将a
封装进Rc<T>
中这样就可以创建都引用a
的有着不同开头的列表b
和c
,类似列表 15-12 中所做的那样。
+一旦创建了shared_list
、b
和c
,接下来就可以通过解引用Rc<T>
和对RefCell
调用borrow_mut
来将 10 与 5 相加了。
+当打印出shared_list
、b
和c
时,可以看到他们都拥有被修改的值 15:
+shared_list after = Cons(RefCell { value: 15 }, Nil)
+b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
+c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
+
+这是非常巧妙的!通过使用RefCell<T>
,我们可以拥有一个表面上不可变的List
,不过可以使用RefCell<T>
中提供内部可变性的方法来在需要时修改数据。RefCell<T>
的运行时借用规则检查也确实保护我们免于出现数据竞争,而且我们也决定牺牲一些速度来换取数据结构的灵活性。
+RefCell<T>
并不是标准库中唯一提供内部可变性的类型。Cell<T>
有点类似,不过不同于RefCell<T>
那样提供内部值的引用,其值被拷贝进和拷贝出Cell<T>
。Mutex<T>
提供线程间安全的内部可变性,下一章并发会讨论它的应用。请查看标准库来获取更多细节和不同类型的区别。
diff --git a/docs/ch15-06-reference-cycles.html b/docs/ch15-06-reference-cycles.html
index cda577a..2ede239 100644
--- a/docs/ch15-06-reference-cycles.html
+++ b/docs/ch15-06-reference-cycles.html
@@ -67,7 +67,86 @@
-
+
+
+ch15-06-reference-cycles.md
+
+commit c49e5ee8859f8eb8f8867cbeafbdf5b802aa5894
+
+
我们讨论过 Rust 做出的一些保证,例如永远也不会遇到一个空值,而且数据竞争也会在编译时被阻止。Rust 的内存安全保证也使其更难以制造从不被清理的内存,这被称为内存泄露。然而 Rust 并不是不可能出现内存泄漏,避免内存泄露并不是 Rust 的保证之一。换句话说,内存泄露是安全的。
+
在使用Rc<T>
和RefCell<T>
时,有可能创建循环引用,这时各个项相互引用并形成环。这是不好的因为每一项的引用计数将永远也到不了 0,其值也永远也不会被丢弃。让我们看看这是如何发生的以及如何避免它。
+
在列表 15-16 中,我们将使用列表 15-5 中List
定义的另一个变体。我们将回到储存i32
值作为Cons
成员的第一个元素。现在Cons
成员的第二个元素是RefCell<Rc<List>>
:这时就不能修改i32
值了,但是能够修改Cons
成员指向的哪个List
。还需要增加一个tail
方法来方便我们在拥有一个Cons
成员时访问第二个项:
+
Filename: src/main.rs
+
#[derive(Debug)]
+enum List {
+ Cons(i32, RefCell<Rc<List>>),
+ Nil,
+}
+
+impl List {
+ fn tail(&self) -> Option<&RefCell<Rc<List>>> {
+ match *self {
+ Cons(_, ref item) => Some(item),
+ Nil => None,
+ }
+ }
+}
+
+
Listing 15-16: A cons list definition that holds a
+RefCell
so that we can modify what a Cons
variant is referring to
+
接下来,在列表 15-17 中,我们将在变量a
中创建一个List
值,其内部是一个5, Nil
的列表。接着在变量b
创建一个值 10 和指向a
中列表的List
值。最后修改a
指向b
而不是Nil
,这会创建一个循环:
+
Filename: src/main.rs
+
# #[derive(Debug)]
+# enum List {
+# Cons(i32, RefCell<Rc<List>>),
+# Nil,
+# }
+#
+# impl List {
+# fn tail(&self) -> Option<&RefCell<Rc<List>>> {
+# match *self {
+# Cons(_, ref item) => Some(item),
+# Nil => None,
+# }
+# }
+# }
+#
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+
+ let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
+
+ println!("a initial rc count = {}", Rc::strong_count(&a));
+ println!("a next item = {:?}", a.tail());
+
+ let b = Rc::new(Cons(10, RefCell::new(a.clone())));
+
+ println!("a rc count after b creation = {}", Rc::strong_count(&a));
+ println!("b initial rc count = {}", Rc::strong_count(&b));
+ println!("b next item = {:?}", b.tail());
+
+ if let Some(ref link) = a.tail() {
+ *link.borrow_mut() = b.clone();
+ }
+
+ println!("b rc count after changing a = {}", Rc::strong_count(&b));
+ println!("a rc count after changing a = {}", Rc::strong_count(&a));
+
+ // Uncomment the next line to see that we have a cycle; it will
+ // overflow the stack
+ // println!("a next item = {:?}", a.tail());
+}
+
+
Listing 15-17: Creating a reference cycle of two List
+values pointing to each other
+
使用tail
方法来获取a
中RefCell
的引用,并将其放入变量link
中。接着对RefCell
使用borrow_mut
方法将其中的值从存放Nil
值的Rc
改为b
中的Rc
。这创建了一个看起来像图 15-18 所示的引用循环:
+
+
Figure 15-18: A reference cycle of lists a
and b
+pointing to each other
+
diff --git a/docs/print.html b/docs/print.html
index fe26d18..8398e6d 100644
--- a/docs/print.html
+++ b/docs/print.html
@@ -8523,7 +8523,7 @@ rc after c goes out of scope = 2
commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
-内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用unsafe
代码来绕过 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的unsafe
代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
+内部可变性(Interior mutability)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用unsafe
代码来模糊 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的unsafe
代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
让我们通过遵循内部可变性模式的RefCell<T>
类型来开始探索。
不同于Rc<T>
,RefCell<T>
代表其数据的唯一的所有权。那么是什么让RefCell<T>
不同于像Box<T>
这样的类型呢?回忆一下第四章所学的借用规则:
@@ -8542,6 +8542,192 @@ commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
因为一些分析是不可能的,Rust 编译器在其不确定的时候甚至都不尝试猜测,所以说它是保守的而且有时会拒绝事实上不会违反 Rust 保证的正确的程序。换句话说,如果 Rust 接受不正确的程序,那么人们也就不会相信 Rust 所做的保证了。如果 Rust 拒绝正确的程序,会给程序员带来不变,但不会带来灾难。RefCell<T>
正是用于当你知道代码遵守借用规则,而编译器不能理解的时候。
类似于Rc<T>
,RefCell<T>
只能用于单线程场景。在并发章节会介绍如何在多线程程序中使用RefCell<T>
的功能。现在所有你需要知道的就是如果尝试在多线程上下文中使用RefCell<T>
,会得到一个编译错误。
对于引用,可以使用&
和&mut
语法来分别创建不可变和可变的引用。不过对于RefCell<T>
,我们使用borrow
和borrow_mut
方法,它是RefCell<T>
拥有的安全 API 的一部分。borrow
返回Ref
类型的智能指针,而borrow_mut
返回RefMut
类型的智能指针。这两个类型实现了Deref
所以可以被当作常规引用处理。Ref
和RefMut
动态的借用所有权,而他们的Drop
实现也动态的释放借用。
+列表 15-14 展示了如何使用RefCell<T>
来使函数不可变的和可变的借用它的参数。注意data
变量使用let data
而不是let mut data
来声明为不可变的,而a_fn_that_mutably_borrows
则允许可变的借用数据并修改它!
+Filename: src/main.rs
+use std::cell::RefCell;
+
+fn a_fn_that_immutably_borrows(a: &i32) {
+ println!("a is {}", a);
+}
+
+fn a_fn_that_mutably_borrows(b: &mut i32) {
+ *b += 1;
+}
+
+fn demo(r: &RefCell<i32>) {
+ a_fn_that_immutably_borrows(&r.borrow());
+ a_fn_that_mutably_borrows(&mut r.borrow_mut());
+ a_fn_that_immutably_borrows(&r.borrow());
+}
+
+fn main() {
+ let data = RefCell::new(5);
+ demo(&data);
+}
+
+Listing 15-14: Using RefCell<T>
, borrow
, and
+borrow_mut
+这个例子打印出:
+a is 5
+a is 6
+
+在main
函数中,我们新声明了一个包含值 5 的RefCell<T>
,并储存在变量data
中,声明时并没有使用mut
关键字。接着使用data
的一个不可变引用来调用demo
函数:对于main
函数而言data
是不可变的!
+在demo
函数中,通过调用borrow
方法来获取到RefCell<T>
中值的不可变引用,并使用这个不可变引用调用了a_fn_that_immutably_borrows
函数。更为有趣的是,可以通过borrow_mut
方法来获取RefCell<T>
中值的可变引用,而a_fn_that_mutably_borrows
函数就允许修改这个值。可以看到下一次调用a_fn_that_immutably_borrows
时打印出的值是 6 而不是 5。
+
+回忆一下第四章因为借用规则,尝试使用常规引用在同一作用域中创建两个可变引用的代码无法编译:
+let mut s = String::from("hello");
+
+let r1 = &mut s;
+let r2 = &mut s;
+
+这会得到一个编译错误:
+error[E0499]: cannot borrow `s` as mutable more than once at a time
+ -->
+ |
+5 | let r1 = &mut s;
+ | - first mutable borrow occurs here
+6 | let r2 = &mut s;
+ | ^ second mutable borrow occurs here
+7 | }
+ | - first borrow ends here
+
+与此相反,使用RefCell<T>
并在同一作用域调用两次borrow_mut
的代码是可以编译的,不过它会在运行时 panic。如下代码:
+use std::cell::RefCell;
+
+fn main() {
+ let s = RefCell::new(String::from("hello"));
+
+ let r1 = s.borrow_mut();
+ let r2 = s.borrow_mut();
+}
+
+能够编译不过在cargo run
运行时会出现如下错误:
+ Finished dev [unoptimized + debuginfo] target(s) in 0.83 secs
+ Running `target/debug/refcell`
+thread 'main' panicked at 'already borrowed: BorrowMutError',
+/stable-dist-rustc/build/src/libcore/result.rs:868
+note: Run with `RUST_BACKTRACE=1` for a backtrace.
+
+这个运行时BorrowMutError
类似于编译错误:它表明已经可变的借用过s
一次了,所以不允许再次借用它。我们并没有绕过借用规则,只是选择让 Rust 在运行时而不是编译时执行他们。你可以选择在任何时候任何地方使用RefCell<T>
,不过除了不得不编写很多RefCell
之外,最终还是可能会发现其中的问题(可能是在生产环境而不是开发环境)。另外,在运行时检查借用规则有性能惩罚。
+
+那么为什么要权衡考虑选择引入RefCell<T>
呢?好吧,还记得我们说过Rc<T>
只能拥有一个T
的不可变引用吗?考虑到RefCell<T>
是不可变的,但是拥有内部可变性,可以将Rc<T>
与RefCell<T>
结合来创造一个既有引用计数又可变的类型。列表 15-15 展示了一个这么做的例子,再次回到列表 15-5 中的 cons list。在这个例子中,不同于在 cons list 中储存i32
值,我们储存一个Rc<RefCell<i32>>
值。希望储存这个类型是因为其可以拥有不属于列表一部分的这个值的所有者(Rc<T>
提供的多个所有者功能),而且还可以改变内部的i32
值(RefCell<T>
提供的内部可变性功能):
+Filename: src/main.rs
+#[derive(Debug)]
+enum List {
+ Cons(Rc<RefCell<i32>>, Rc<List>),
+ Nil,
+}
+
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+ let value = Rc::new(RefCell::new(5));
+
+ let a = Cons(value.clone(), Rc::new(Nil));
+ let shared_list = Rc::new(a);
+
+ let b = Cons(Rc::new(RefCell::new(6)), shared_list.clone());
+ let c = Cons(Rc::new(RefCell::new(10)), shared_list.clone());
+
+ *value.borrow_mut() += 10;
+
+ println!("shared_list after = {:?}", shared_list);
+ println!("b after = {:?}", b);
+ println!("c after = {:?}", c);
+}
+
+Listing 15-15: Using Rc<RefCell<i32>>
to create a
+List
that we can mutate
+我们创建了一个值,它是Rc<RefCell<i32>>
的实例。将其储存在变量value
中因为我们希望之后能直接访问它。接着在a
中创建了一个拥有存放了value
值的Cons
成员的List
,而且value
需要被克隆因为我们希望除了a
之外还拥有value
的所有权。接着将a
封装进Rc<T>
中这样就可以创建都引用a
的有着不同开头的列表b
和c
,类似列表 15-12 中所做的那样。
+一旦创建了shared_list
、b
和c
,接下来就可以通过解引用Rc<T>
和对RefCell
调用borrow_mut
来将 10 与 5 相加了。
+当打印出shared_list
、b
和c
时,可以看到他们都拥有被修改的值 15:
+shared_list after = Cons(RefCell { value: 15 }, Nil)
+b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
+c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
+
+这是非常巧妙的!通过使用RefCell<T>
,我们可以拥有一个表面上不可变的List
,不过可以使用RefCell<T>
中提供内部可变性的方法来在需要时修改数据。RefCell<T>
的运行时借用规则检查也确实保护我们免于出现数据竞争,而且我们也决定牺牲一些速度来换取数据结构的灵活性。
+RefCell<T>
并不是标准库中唯一提供内部可变性的类型。Cell<T>
有点类似,不过不同于RefCell<T>
那样提供内部值的引用,其值被拷贝进和拷贝出Cell<T>
。Mutex<T>
提供线程间安全的内部可变性,下一章并发会讨论它的应用。请查看标准库来获取更多细节和不同类型的区别。
+
+
+ch15-06-reference-cycles.md
+
+commit c49e5ee8859f8eb8f8867cbeafbdf5b802aa5894
+
+我们讨论过 Rust 做出的一些保证,例如永远也不会遇到一个空值,而且数据竞争也会在编译时被阻止。Rust 的内存安全保证也使其更难以制造从不被清理的内存,这被称为内存泄露。然而 Rust 并不是不可能出现内存泄漏,避免内存泄露并不是 Rust 的保证之一。换句话说,内存泄露是安全的。
+在使用Rc<T>
和RefCell<T>
时,有可能创建循环引用,这时各个项相互引用并形成环。这是不好的因为每一项的引用计数将永远也到不了 0,其值也永远也不会被丢弃。让我们看看这是如何发生的以及如何避免它。
+在列表 15-16 中,我们将使用列表 15-5 中List
定义的另一个变体。我们将回到储存i32
值作为Cons
成员的第一个元素。现在Cons
成员的第二个元素是RefCell<Rc<List>>
:这时就不能修改i32
值了,但是能够修改Cons
成员指向的哪个List
。还需要增加一个tail
方法来方便我们在拥有一个Cons
成员时访问第二个项:
+Filename: src/main.rs
+#[derive(Debug)]
+enum List {
+ Cons(i32, RefCell<Rc<List>>),
+ Nil,
+}
+
+impl List {
+ fn tail(&self) -> Option<&RefCell<Rc<List>>> {
+ match *self {
+ Cons(_, ref item) => Some(item),
+ Nil => None,
+ }
+ }
+}
+
+Listing 15-16: A cons list definition that holds a
+RefCell
so that we can modify what a Cons
variant is referring to
+接下来,在列表 15-17 中,我们将在变量a
中创建一个List
值,其内部是一个5, Nil
的列表。接着在变量b
创建一个值 10 和指向a
中列表的List
值。最后修改a
指向b
而不是Nil
,这会创建一个循环:
+Filename: src/main.rs
+# #[derive(Debug)]
+# enum List {
+# Cons(i32, RefCell<Rc<List>>),
+# Nil,
+# }
+#
+# impl List {
+# fn tail(&self) -> Option<&RefCell<Rc<List>>> {
+# match *self {
+# Cons(_, ref item) => Some(item),
+# Nil => None,
+# }
+# }
+# }
+#
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+
+ let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
+
+ println!("a initial rc count = {}", Rc::strong_count(&a));
+ println!("a next item = {:?}", a.tail());
+
+ let b = Rc::new(Cons(10, RefCell::new(a.clone())));
+
+ println!("a rc count after b creation = {}", Rc::strong_count(&a));
+ println!("b initial rc count = {}", Rc::strong_count(&b));
+ println!("b next item = {:?}", b.tail());
+
+ if let Some(ref link) = a.tail() {
+ *link.borrow_mut() = b.clone();
+ }
+
+ println!("b rc count after changing a = {}", Rc::strong_count(&b));
+ println!("a rc count after changing a = {}", Rc::strong_count(&a));
+
+ // Uncomment the next line to see that we have a cycle; it will
+ // overflow the stack
+ // println!("a next item = {:?}", a.tail());
+}
+
+Listing 15-17: Creating a reference cycle of two List
+values pointing to each other
+使用tail
方法来获取a
中RefCell
的引用,并将其放入变量link
中。接着对RefCell
使用borrow_mut
方法将其中的值从存放Nil
值的Rc
改为b
中的Rc
。这创建了一个看起来像图 15-18 所示的引用循环:
+
+Figure 15-18: A reference cycle of lists a
and b
+pointing to each other
diff --git a/src/ch15-05-interior-mutability.md b/src/ch15-05-interior-mutability.md
index 5883378..c6c2716 100644
--- a/src/ch15-05-interior-mutability.md
+++ b/src/ch15-05-interior-mutability.md
@@ -4,7 +4,7 @@
>
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
-**内部可变性**(*Interior mutability*)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用`unsafe`代码来绕过 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的`unsafe`代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
+**内部可变性**(*Interior mutability*)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用`unsafe`代码来模糊 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的`unsafe`代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
让我们通过遵循内部可变性模式的`RefCell`类型来开始探索。
@@ -27,3 +27,146 @@ Rust 编译器执行的静态分析时天生保守的。代码的一些属性则
对于引用,可以使用`&`和`&mut`语法来分别创建不可变和可变的引用。不过对于`RefCell`,我们使用`borrow`和`borrow_mut`方法,它是`RefCell`拥有的安全 API 的一部分。`borrow`返回`Ref`类型的智能指针,而`borrow_mut`返回`RefMut`类型的智能指针。这两个类型实现了`Deref`所以可以被当作常规引用处理。`Ref`和`RefMut`动态的借用所有权,而他们的`Drop`实现也动态的释放借用。
+列表 15-14 展示了如何使用`RefCell`来使函数不可变的和可变的借用它的参数。注意`data`变量使用`let data`而不是`let mut data`来声明为不可变的,而`a_fn_that_mutably_borrows`则允许可变的借用数据并修改它!
+
+Filename: src/main.rs
+
+```rust
+use std::cell::RefCell;
+
+fn a_fn_that_immutably_borrows(a: &i32) {
+ println!("a is {}", a);
+}
+
+fn a_fn_that_mutably_borrows(b: &mut i32) {
+ *b += 1;
+}
+
+fn demo(r: &RefCell) {
+ a_fn_that_immutably_borrows(&r.borrow());
+ a_fn_that_mutably_borrows(&mut r.borrow_mut());
+ a_fn_that_immutably_borrows(&r.borrow());
+}
+
+fn main() {
+ let data = RefCell::new(5);
+ demo(&data);
+}
+```
+
+Listing 15-14: Using `RefCell`, `borrow`, and
+`borrow_mut`
+
+这个例子打印出:
+
+```
+a is 5
+a is 6
+```
+
+在`main`函数中,我们新声明了一个包含值 5 的`RefCell`,并储存在变量`data`中,声明时并没有使用`mut`关键字。接着使用`data`的一个不可变引用来调用`demo`函数:对于`main`函数而言`data`是不可变的!
+
+在`demo`函数中,通过调用`borrow`方法来获取到`RefCell`中值的不可变引用,并使用这个不可变引用调用了`a_fn_that_immutably_borrows`函数。更为有趣的是,可以通过`borrow_mut`方法来获取`RefCell`中值的**可变**引用,而`a_fn_that_mutably_borrows`函数就允许修改这个值。可以看到下一次调用`a_fn_that_immutably_borrows`时打印出的值是 6 而不是 5。
+
+### `RefCell`在运行时检查借用规则
+
+回忆一下第四章因为借用规则,尝试使用常规引用在同一作用域中创建两个可变引用的代码无法编译:
+
+```rust,ignore
+let mut s = String::from("hello");
+
+let r1 = &mut s;
+let r2 = &mut s;
+```
+
+这会得到一个编译错误:
+
+```
+error[E0499]: cannot borrow `s` as mutable more than once at a time
+ -->
+ |
+5 | let r1 = &mut s;
+ | - first mutable borrow occurs here
+6 | let r2 = &mut s;
+ | ^ second mutable borrow occurs here
+7 | }
+ | - first borrow ends here
+```
+
+与此相反,使用`RefCell`并在同一作用域调用两次`borrow_mut`的代码是**可以**编译的,不过它会在运行时 panic。如下代码:
+
+```rust,should_panic
+use std::cell::RefCell;
+
+fn main() {
+ let s = RefCell::new(String::from("hello"));
+
+ let r1 = s.borrow_mut();
+ let r2 = s.borrow_mut();
+}
+```
+
+能够编译不过在`cargo run`运行时会出现如下错误:
+
+```
+ Finished dev [unoptimized + debuginfo] target(s) in 0.83 secs
+ Running `target/debug/refcell`
+thread 'main' panicked at 'already borrowed: BorrowMutError',
+/stable-dist-rustc/build/src/libcore/result.rs:868
+note: Run with `RUST_BACKTRACE=1` for a backtrace.
+```
+
+这个运行时`BorrowMutError`类似于编译错误:它表明已经可变的借用过`s`一次了,所以不允许再次借用它。我们并没有绕过借用规则,只是选择让 Rust 在运行时而不是编译时执行他们。你可以选择在任何时候任何地方使用`RefCell`,不过除了不得不编写很多`RefCell`之外,最终还是可能会发现其中的问题(可能是在生产环境而不是开发环境)。另外,在运行时检查借用规则有性能惩罚。
+
+### 结合`Rc`和`RefCell`来拥有多个可变数据所有者
+
+那么为什么要权衡考虑选择引入`RefCell`呢?好吧,还记得我们说过`Rc`只能拥有一个`T`的不可变引用吗?考虑到`RefCell`是不可变的,但是拥有内部可变性,可以将`Rc`与`RefCell`结合来创造一个既有引用计数又可变的类型。列表 15-15 展示了一个这么做的例子,再次回到列表 15-5 中的 cons list。在这个例子中,不同于在 cons list 中储存`i32`值,我们储存一个`Rc>`值。希望储存这个类型是因为其可以拥有不属于列表一部分的这个值的所有者(`Rc`提供的多个所有者功能),而且还可以改变内部的`i32`值(`RefCell`提供的内部可变性功能):
+
+Filename: src/main.rs
+
+```rust
+#[derive(Debug)]
+enum List {
+ Cons(Rc>, Rc),
+ Nil,
+}
+
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+ let value = Rc::new(RefCell::new(5));
+
+ let a = Cons(value.clone(), Rc::new(Nil));
+ let shared_list = Rc::new(a);
+
+ let b = Cons(Rc::new(RefCell::new(6)), shared_list.clone());
+ let c = Cons(Rc::new(RefCell::new(10)), shared_list.clone());
+
+ *value.borrow_mut() += 10;
+
+ println!("shared_list after = {:?}", shared_list);
+ println!("b after = {:?}", b);
+ println!("c after = {:?}", c);
+}
+```
+
+Listing 15-15: Using `Rc>` to create a
+`List` that we can mutate
+
+我们创建了一个值,它是`Rc>`的实例。将其储存在变量`value`中因为我们希望之后能直接访问它。接着在`a`中创建了一个拥有存放了`value`值的`Cons`成员的`List`,而且`value`需要被克隆因为我们希望除了`a`之外还拥有`value`的所有权。接着将`a`封装进`Rc`中这样就可以创建都引用`a`的有着不同开头的列表`b`和`c`,类似列表 15-12 中所做的那样。
+
+一旦创建了`shared_list`、`b`和`c`,接下来就可以通过解引用`Rc`和对`RefCell`调用`borrow_mut`来将 10 与 5 相加了。
+
+当打印出`shared_list`、`b`和`c`时,可以看到他们都拥有被修改的值 15:
+
+```
+shared_list after = Cons(RefCell { value: 15 }, Nil)
+b after = Cons(RefCell { value: 6 }, Cons(RefCell { value: 15 }, Nil))
+c after = Cons(RefCell { value: 10 }, Cons(RefCell { value: 15 }, Nil))
+```
+
+这是非常巧妙的!通过使用`RefCell`,我们可以拥有一个表面上不可变的`List`,不过可以使用`RefCell`中提供内部可变性的方法来在需要时修改数据。`RefCell`的运行时借用规则检查也确实保护我们免于出现数据竞争,而且我们也决定牺牲一些速度来换取数据结构的灵活性。
+
+`RefCell`并不是标准库中唯一提供内部可变性的类型。`Cell`有点类似,不过不同于`RefCell`那样提供内部值的引用,其值被拷贝进和拷贝出`Cell`。`Mutex`提供线程间安全的内部可变性,下一章并发会讨论它的应用。请查看标准库来获取更多细节和不同类型的区别。
\ No newline at end of file
diff --git a/src/ch15-06-reference-cycles.md b/src/ch15-06-reference-cycles.md
index e69de29..773a305 100644
--- a/src/ch15-06-reference-cycles.md
+++ b/src/ch15-06-reference-cycles.md
@@ -0,0 +1,94 @@
+## 引用循环和内存泄漏是安全的
+
+> [ch15-06-reference-cycles.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-06-reference-cycles.md)
+>
+> commit c49e5ee8859f8eb8f8867cbeafbdf5b802aa5894
+
+我们讨论过 Rust 做出的一些保证,例如永远也不会遇到一个空值,而且数据竞争也会在编译时被阻止。Rust 的内存安全保证也使其更难以制造从不被清理的内存,这被称为**内存泄露**。然而 Rust 并不是**不可能**出现内存泄漏,避免内存泄露**并**不是 Rust 的保证之一。换句话说,内存泄露是安全的。
+
+在使用`Rc`和`RefCell`时,有可能创建循环引用,这时各个项相互引用并形成环。这是不好的因为每一项的引用计数将永远也到不了 0,其值也永远也不会被丢弃。让我们看看这是如何发生的以及如何避免它。
+
+在列表 15-16 中,我们将使用列表 15-5 中`List`定义的另一个变体。我们将回到储存`i32`值作为`Cons`成员的第一个元素。现在`Cons`成员的第二个元素是`RefCell>`:这时就不能修改`i32`值了,但是能够修改`Cons`成员指向的哪个`List`。还需要增加一个`tail`方法来方便我们在拥有一个`Cons`成员时访问第二个项:
+
+Filename: src/main.rs
+
+```rust,ignore
+#[derive(Debug)]
+enum List {
+ Cons(i32, RefCell>),
+ Nil,
+}
+
+impl List {
+ fn tail(&self) -> Option<&RefCell>> {
+ match *self {
+ Cons(_, ref item) => Some(item),
+ Nil => None,
+ }
+ }
+}
+```
+
+Listing 15-16: A cons list definition that holds a
+`RefCell` so that we can modify what a `Cons` variant is referring to
+
+接下来,在列表 15-17 中,我们将在变量`a`中创建一个`List`值,其内部是一个`5, Nil`的列表。接着在变量`b`创建一个值 10 和指向`a`中列表的`List`值。最后修改`a`指向`b`而不是`Nil`,这会创建一个循环:
+
+Filename: src/main.rs
+
+```rust
+# #[derive(Debug)]
+# enum List {
+# Cons(i32, RefCell>),
+# Nil,
+# }
+#
+# impl List {
+# fn tail(&self) -> Option<&RefCell>> {
+# match *self {
+# Cons(_, ref item) => Some(item),
+# Nil => None,
+# }
+# }
+# }
+#
+use List::{Cons, Nil};
+use std::rc::Rc;
+use std::cell::RefCell;
+
+fn main() {
+
+ let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));
+
+ println!("a initial rc count = {}", Rc::strong_count(&a));
+ println!("a next item = {:?}", a.tail());
+
+ let b = Rc::new(Cons(10, RefCell::new(a.clone())));
+
+ println!("a rc count after b creation = {}", Rc::strong_count(&a));
+ println!("b initial rc count = {}", Rc::strong_count(&b));
+ println!("b next item = {:?}", b.tail());
+
+ if let Some(ref link) = a.tail() {
+ *link.borrow_mut() = b.clone();
+ }
+
+ println!("b rc count after changing a = {}", Rc::strong_count(&b));
+ println!("a rc count after changing a = {}", Rc::strong_count(&a));
+
+ // Uncomment the next line to see that we have a cycle; it will
+ // overflow the stack
+ // println!("a next item = {:?}", a.tail());
+}
+```
+
+Listing 15-17: Creating a reference cycle of two `List`
+values pointing to each other
+
+使用`tail`方法来获取`a`中`RefCell`的引用,并将其放入变量`link`中。接着对`RefCell`使用`borrow_mut`方法将其中的值从存放`Nil`值的`Rc`改为`b`中的`Rc`。这创建了一个看起来像图 15-18 所示的引用循环:
+
+
+
+Figure 15-18: A reference cycle of lists `a` and `b`
+pointing to each other
+