You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
trpl-zh-cn/src/ch15-05-interior-mutability.md

30 lines
3.0 KiB

8 years ago
## `RefCell<T>`和内部可变性模式
> [ch15-05-interior-mutability.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch15-05-interior-mutability.md)
> <br>
> commit 3f2a1bd8dbb19cc48b210fc4fb35c305c8d81b56
**内部可变性***Interior mutability*)是 Rust 中的一个设计模式,它允许你即使在有不可变引用时改变数据,这通常是借用规则所不允许。内部可变性模式涉及到在数据结构中使用`unsafe`代码来绕过 Rust 通常的可变性和借用规则。我们还未讲到不安全代码;第十九章会学习他们。内部可变性模式用于当你可以确保代码在运行时也会遵守借用规则,哪怕编译器也不能保证的情况。引入的`unsafe`代码将被封装进安全的 API 中,而外部类型仍然是不可变的。
让我们通过遵循内部可变性模式的`RefCell<T>`类型来开始探索。
### `RefCell<T>`拥有内部可变性
不同于`Rc<T>``RefCell<T>`代表其数据的唯一的所有权。那么是什么让`RefCell<T>`不同于像`Box<T>`这样的类型呢?回忆一下第四章所学的借用规则:
1. 在任意给定时间,**只能**拥有如下中的一个:
* 一个可变引用。
* 任意属性的不可变引用。
2. 引用必须总是有效的。
对于引用和`Box<T>`,借用规则的不可变性作用于编译时。对于`RefCell<T>`,这些不可变性作用于**运行时**。对于引用,如果违反这些规则,会得到一个编译错误。而对于`RefCell<T>`,违反这些规则会`panic!`。
Rust 编译器执行的静态分析时天生保守的。代码的一些属性则不可能通过分析代码发现:其中最著名的就是停机问题(停机问题),这超出了本书的范畴,不过如果你感兴趣的话这是一个值得研究的有趣主题。
因为一些分析是不可能的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`实现也动态的释放借用。