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!" } |