Zach的博客

Cell和RefCell

Mutability

在Rust中,如果我们这样操作一个变量:

1
2
let x = 5;
x = 6; // error

我们就会得到一个编译错误。

如果要更改一个变量,我们需要添加mut关键字:

1
2
let mut x = 5;
x = 6;

我们也可以像下面一样更改一个变量的值:

1
2
3
let mut x = 5;
let y = &mut x;
*y = 4;

注意到y是可变变量x的可变引用,所以我们可以y来更改x的值,但是我们并不能更改y,在这里只有x是可变的。

Interior Mutability & Exterior Mutability

考虑下面一个例子:

1
2
3
4
use std::sync::Arc;

let x = Arc::new(5);
let y = x.clone();

clone函数被调用后,Arc<T>会增加它的引用计数,这咋一看和x是不可变的变量矛盾了不是吗?在这里,Arc<T>暴露在用户面前的是不可变的,但是其内部却是可以改变的,因为它使用了RefCell。标准库对CellRefCell的定义如下:

1
Values of the Cell<T> and RefCell<T> types may be mutated through shared references (i.e. the common &T type), whereas most Rust types can only be mutated through unique (&mut T) references. We say that Cell<T> and RefCell<T> provide 'interior mutability', in contrast with typical Rust types that exhibit 'inherited mutability'.

也就是说我们可以像这样操作变量:

1
2
3
4
5
use std::cell::RefCell;

let x = RefCell::new(42);
*x.borrow_mut() = 7;
assert_eq!(*x.borrow_mut(), 7);

borrow_mut将变量内部的可变借给了用户,这样我们就可以修改x的值了。还记得Rust的借用规则吗?我们说Rust的一个变量可以有多个不可变的借用,但是有且仅有一个可变的借用,所以如果我们这样操作:

1
2
3
4
5
6
use std::cell::RefCell;

let x = RefCell::new(42);

let y = x.borrow_mut();
let z = x.borrow_mut();

那么这段代码就会在运行时崩溃(panic in runtime),这也说明了一点:RefCell在运行时执行了Rust的借用规则。

RefCellCell 的区别在于,Cell只能用于那么实现了CopyTrait的类型,其余的类型使用RefCellRefCell让程序在修改变量时必须先获得一个写锁。

Cell和RefCell的运用

如果我们自定义了一个变量,变量的某些属性是可以修改的,而某些属性不能修改,那么这个时候就可以用Cell 或者RefCell了:

1
2
3
4
5
6
7
8
9
10
11
12
use std::cell::Cell;

struct Point {
x: i32,
y: Cell<i32>,
}

let point = Point { x: 5, y: Cell::new(6) };

point.y.set(7);

println!("y: {:?}", point.y);

参考

The Rust Programming Language, Mutability