Choose a decision tree
需要共享数据吗?
│
├─ 否 → 单 owner
│ ├─ 需要堆分配? → Box<T>
│ └─ 栈上即可? → 直接值类型
│
└─ 是 → 需要共享
│
├─ 单线程?
│ ├─ 可变? → Rc<RefCell<T>>
│ └─ 只读? → Rc<T>
│
└─ 多线程?
├─ 可变? → Arc<Mutex<T>> 或 Arc<RwLock<T>>
└─ 只读? → Arc<T>
Smart pointer comparison
| Type |
Ownership |
Thread safety |
Use case |
Box<T> |
Single owner |
Yes |
Heap allocation, recursive types, trait object |
Rc<T> |
Shared |
No |
Single-threaded sharing, avoid clone |
Arc<T> |
Shared |
Yes |
Multi-threaded sharing, read-only data |
Weak<T> |
Weak reference |
- |
Break reference cycles |
RefCell<T> |
Single owner |
No |
Runtime borrow checking |
Cell<T> |
Single owner |
No |
Interior mutability for Copy types |
Common errors and solutions
Rc reference cycle leak
// ❌ 内存泄漏:两个 Rc 互相引用
struct Node {
value: i32,
next: Option<Rc<Node>>,
}
// ✅ 解决方案:使用 Weak 打破循环
struct Node {
value: i32,
next: Option<Weak<Node>>,
}
RefCell panic
// ❌ 运行时 panic:双重可变借用
let cell = RefCell::new(vec![1, 2, 3]);
let mut_borrow = cell.borrow_mut();
let another_borrow = cell.borrow(); // panic!
// ✅ 解决方案:使用 try_borrow
if let Ok(mut_borrow) = cell.try_borrow_mut() {
// 安全使用
}
Arc overhead complaints
// ❌ 不必要的 Arc:单线程环境
let shared = Arc::new(data);
// ✅ 单线程用 Rc
let shared = Rc::new(data);
// ❌ 多线程不必要的原子操作
// 如果确定不需要跨线程共享,就不要用 Arc
Choosing interior mutability
// T 是 Copy 类型 → Cell
struct Counter {
count: Cell<u32>,
}
// T 不是 Copy → RefCell
struct Container {
items: RefCell<Vec<Item>>,
}
// 多线程 → Mutex 或 RwLock
struct SharedContainer {
items: Mutex<Vec<Item>>,
}
RAII and Drop
struct File {
handle: std::fs::File,
}
impl Drop for File {
fn drop(&mut self) {
// 自动释放资源
println!("File closed");
}
}
// 使用 guard pattern 确保清理
struct Guard<'a> {
resource: &'a Resource,
}
impl Drop for Guard<'_> {
fn drop(&mut self) {
self.resource.release();
}
}
Performance tips
| Scenario |
Recommendation |
| Many small objects |
Rc::make_mut() Avoid clone |
| Frequent reads |
RwLock is better than Mutex is better |
| Counter |
Use AtomicU64 instead of Mutex<u64> |
| Cache |
Consider moka or cached crate |
When not to use smart pointers
- Stack data is sufficient → use value types
- Borrowing suffices → use references
&T
- Simple lifetimes → don't over-abstract