
本章系统讲解 Rust 的智能指针与内在可变性(Interior Mutability):掌握何时使用 Box、Rc/Arc,何时选择 Cell/RefCell 或并发环境下的 Mutex/RwLock,写出既安全又高性能的工程代码。
Box)、引用计数共享(Rc/Arc)、运行时借用检查(RefCell)、并发互斥/读写锁(Mutex/RwLock)。enum List { Cons(i32, Box<List>), Nil }
fn main() {
let x = Box::new(42);
println!("{}", x);
}要点:Box<T> 实现了 Deref,可像 &T 一样使用;析构时自动释放堆内存。
Rc<T>:单线程下的共享,非并发安全;Arc<T>:原子引用计数,并发安全,在多线程中共享。use std::rc::Rc;
let a = Rc::new(String::from("hello"));
let b = Rc::clone(&a); // 增加计数
println!("{} {}", a, b);RefCell/Mutex)。Cell<T>:按值替换(Copy 友好),提供 get/set/replace;RefCell<T>:运行时借用检查,提供 borrow()/borrow_mut(),违反规则会 panic。use std::cell::RefCell;
struct Counter { inner: RefCell<i32> }
impl Counter { fn inc(&self) { *self.inner.borrow_mut() += 1; } }
fn main() {
let c = Counter { inner: RefCell::new(0) };
c.inc(); c.inc();
println!("{}", c.inner.borrow());
}适用:需要在 &self 方法中修改内部状态(如缓存、延迟初始化),但仅限单线程。
Mutex<T>:互斥锁,独占写;RwLock<T>:读写锁,多读一写。use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..4 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || { *c.lock().unwrap() += 1; }));
}
for h in handles { h.join().unwrap(); }
println!("{}", *counter.lock().unwrap());
}注意:
lock() 返回守卫,出作用域自动解锁;Mutex 与 RwLock 都是 Sync,配合 Arc 在多线程共享所有权。Rc<RefCell<T>>;Arc<Mutex<T>> / Arc<RwLock<T>>。use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug)]
struct Node { val: i32, next: Option<Rc<RefCell<Node>>> }
fn main() {
let n1 = Rc::new(RefCell::new(Node { val: 1, next: None }));
let n2 = Rc::new(RefCell::new(Node { val: 2, next: Some(Rc::clone(&n1)) }));
n1.borrow_mut().next = Some(Rc::clone(&n2)); // 可构建共享环
println!("n1={:?}", n1);
}
Rc 使用弱引用 Weak 断环)。use std::rc::{Rc, Weak};
use std::cell::RefCell;
#[derive(Debug)]
struct Node { val: i32, parent: RefCell<Weak<Node>>, children: RefCell<Vec<Rc<Node>>> }Weak<T> 不增加强计数,升级时用 upgrade() -> Option<Rc<T>>;需求 | 单线程 | 多线程 |
|---|---|---|
仅堆分配 | Box<T> | Box<T> |
共享只读 | Rc<T> | Arc<T> |
共享可变 | Rc<RefCell<T>> | Arc<Mutex<T>> / Arc<RwLock<T>> |
可选按值替换 | Cell<T> | (不适用并发) |
防环 | Weak<T> | Weak<T>(配合 Arc) |
RefCell 里重复 borrow_mut() 导致运行时 panic → 缩小作用域或重构逻辑;Mutex 守卫持有时间过长 → 分离计算与持锁阶段,尽量在锁外做重活;Rc 用在多线程 → 崩溃,改用 Arc;Rc,子指回父用 Weak;Arc<RefCell<T>>(多线程 + 非线程安全)→ 用 Arc<Mutex<T>> 或 Arc<RwLock<T>>。use std::sync::{Arc, Mutex, RwLock};
use std::collections::HashMap;
use std::thread;
fn counter_demo() {
// 明确 Mutex 保护的类型是 usize
let c = Arc::new(Mutex::new(0usize));
let mut hs = vec![];
for _ in 0..8 {
let cc = Arc::clone(&c);
hs.push(thread::spawn(move || {
// 锁定并修改值
*cc.lock().unwrap() += 1;
}));
}
// 等待所有线程完成
for h in hs {
h.join().unwrap();
}
// 输出最终计数
println!("count={}", *c.lock().unwrap());
}
fn cache_demo() {
// 显式指定 HashMap 的键值类型为 String, String
let cache: Arc<RwLock<HashMap<String, String>>> = Arc::new(RwLock::new(HashMap::new()));
{
// 获取写锁并插入数据
let mut w = cache.write().unwrap();
w.insert("k".into(), "v".into());
}
// 获取读锁并读取数据
let r = cache.read().unwrap();
println!("{:?}", r.get("k"));
}
fn main() {
counter_demo();
cache_demo();
}
Rc<RefCell<T>> 实现一个可共享修改的双向链表节点,思考如何用 Weak 打破环;Arc<RwLock<...>>,对比读多写少的性能;Counter 增加 fetch_add 与 reset 接口,确保不会死锁(合理划分锁粒度)。小结:
Box 管堆分配;Rc/Arc 管共享;Cell/RefCell 管单线程可变;Mutex/RwLock 管并发可变;Weak 断环、PhantomData 标注借用、守卫对象 RAII 释放;