Cell<T>
Cell 和 RefCell 在功能上没有区别,区别在于 Cell<T> 适用于 T 实现 Copy 的情况:
1 | use std::cell::Cell; |
输出:
1 | asdf,qwer |
因为实现了
Copy,所以"asdf"这个值被复制了一份,给了one变量.
Cell 没有可变引用
1 | use std::cell::Cell; |
这里,
y, z都是x的不可变引用,调用y.set()的时候,会自动 dereference 为
1 (&x).set() ===> x.set()因此,
y, z调用set()都会修改x这个指针指向的数据,因此输出为
Cell::from_mut(), Cell::as_slice_of_cells() 使用技巧
Cell::from_mut(),该方法将&mut T转为&Cell<T>Cell::as_slice_of_cells(),该方法将&Cell<[T]>转为&[Cell<T>]
1 | // Wrong |
1 | // True |
这个例子里,我们希望通过 iter().filter() 遍历元素,此时迭代器是对元素的不可变借用,因此无法通过不可变借用来修改元素值(因为会违反 Rust 的借用规则)。而 Cell<T> 允许使用 .get() 和 .set() 方法,在不可变借用的上下文里修改值,从而绕过 Rust 的限制。所以我们需要的是 an immutable reference of a list of Cell<T>s.
RefCell<T>
RefCell<T> 解决的问题更进一步:解决可变引用、不可变引用共存的问题。本质只是将编译阶段的共存检查,推迟到运行时阶段,即如果运行时阶段里还是出现了 mutable borrow 和 immutable borrow 同时存在的情况下,依然会报运行时错误 Panic.
| Rust 规则 | 智能指针带来的额外规则 |
|---|---|
| 一个数据只有一个所有者 | Rc<T>/Arc<T> 让一个数据可以拥有多个所有者 |
| 要么多个不可变借用,要么一个可变借用 | RefCell<T> 实现编译期可变、不可变引用共存 |
| 违背规则导致编译错误 | 违背规则导致运行时 panic |
什么是内部可变性?
在某些场景中,一个值可以在其方法内部被修改,同时对于其它代码不可变。例如说考虑下面这个例子:
1 | // 定义在外部库中的特征 |
我们有个变量 x: MsgQueue,我们调用 x.send() 希望修改数据结构里的一部分,但是不希望 x 变量本身被修改。然而,send() 方法的签名是 &self,而且 .push() 修改了 x 的数据,因此 &self 会报错。
这个时候 RefCell<T> 就派上用场了。我们把 msg_cache 用 RefCell<Vec<String>> 包裹,就可以实现保持 x 不可变的情况下修改内部的数据。
1 | // 定义在外部库中的特征 |
1 | use std::cell::RefCell; |
注意这里的 borrow_mut()
Rc + RefCell
一个常见的用法是 Rc<RefCell<T>>,这样,多个 x = Rc<RefCell<T>> 可以共享底层的数据。
1 | use std::cell::RefCell; |
输出:
1 | RefCell { value: "我很善变,还拥有多个主人, oh yeah!" } |