本页目录
Cell, RefCell, Mutex: 内部可变性
Rust 的共享引用(
&T)默认不可变——Cell/RefCell/Mutex 在"共享"的前提下提供了不同程度的内部可变性。Cell 适合 Copy 类型(无运行时开销),RefCell 把借用检查推迟到运行时(panic 报错),Mutex 加上线程安全的锁(Send)。
问题的起源
let x = 5;
let r = &x; // &i32, 不可变
// *r = 6; // ERROR: &T 不能被修改
Rust 的核心规则:通过共享引用(&T)不能修改数据。这防止了数据竞争和 iterator invalidation。但有时逻辑上是安全的修改却被规则拒绝了——比如缓存计算结果、内部引用计数。内部可变性允许你在拥有 &self 时修改内部状态。
Cell: 替换整个值(Copy 类型专用)
use Cell;
let c = new;
c.set; // 替换内部值 — 不需要 &mut self
let val = c.get; // Copy 出来
Cell 不返回内部引用——get() 总是复制值(要求 T: Copy),set() 总是替换整个值。没有引用泄漏到外部,所以 borrow checker 不会阻止。适用场景:简单的 i32, bool, enum 包装。
RefCell: 运行时 Borrow Check
use RefCell;
let rc = new;
// borrow_mut guard drop → 释放 borrow
let borrow = rc.borrow; // runtime: OK
规则与编译期 borrow checker 相同——aliasing XOR mutation——但检查发生在运行时。违反 → panic!(不是编译错误)。borrow() 返回 Ref<T>(Deref to &T),borrow_mut() 返回 RefMut<T>(Deref to &mut T)。这些 guard 在离开作用域时 drop,释放内部 borrow count。
RefCell 不是 Sync——不能跨线程共享。
Mutex: 内部可变性 + Send + 锁
use Mutex;
let m = new;
let mut guard = m.lock.unwrap; // 阻塞直到获得锁
*guard += 1; // 通过 DerefMut 访问内部值
drop; // 释放锁 (也可依赖 scope exit)
Mutex 结合了 RefCell 的内部可变性 + OS 锁 + Send 保证。底层是 UnsafeCell + OS mutex(pthread_mutex_t on Linux)。lock() 阻塞当前线程直到获得锁,try_lock() 非阻塞,立即返回 Option<MutexGuard>。
三者对比
| Cell | RefCell | Mutex | |
|---|---|---|---|
| 内部可变性 | Y | Y | Y |
| 返回引用 | N (只 copy) | Y (Ref/RefMut) | Y (MutexGuard) |
| 冲突处理 | N/A | panic | block |
| Thread-safe | N | N | Y |
| 检查时机 | 编译期 (no borrow) | 运行时 (panic) | 运行时 (block) |
参考
- Rust Book: Chapter 15.5
- Rustonomicon: UnsafeCell (the primitive behind all interior mutability)
Keywords: Cell, RefCell, Mutex, interior mutability, UnsafeCell, borrow_mut, runtime borrow check