智能指针的内存管理原理
原理
Box<T> 是堆分配的单所有权指针,等同于 C++ 的 unique_ptr。内存布局:栈上 8B 指针指向堆上 T。Box 的 drop 触发时先调用 T::drop,再释放堆内存(通过 __rust_dealloc)。
Rc<T> 使用非原子引用计数(Cell<usize> 计数器),堆上分配时保存 {T, strong_count, weak_count}。clone 增加计数,drop 减少计数,计数归零时释放 T。Rc 不能跨线程(!Send + !Sync)。
Arc<T> 使用原子引用计数(AtomicUsize),安全的跨线程共享。额外开销是 fetch_add / fetch_sub 的 LOCK 前缀(x86)和内存屏障。
RefCell<T> 在运行时检查借用规则:通过 Cell<isize> 跟踪 borrow count。borrow() 读时计数+1,borrow_mut() 写时计数设为 -1。违反规则时触发 panic! 而非编译错误。
Cow<'a, T>:写时复制。持有 &T 引用或 T 的所有权。仅在需要修改时才分配新数据。
语法
// Box — 堆分配单所有权
let b = Box::new(42);
let raw = Box::into_raw(b); // 消费 Box,返回裸指针
unsafe { Box::from_raw(raw); } // 从裸指针重建 Box
// Rc — 单线程引用计数
use std::rc::Rc;
let r = Rc::new(String::from("hi"));
let r2 = Rc::clone(&r); // 计数+1(浅克隆)
assert_eq!(Rc::strong_count(&r), 2);
// Arc — 原子引用计数(线程安全)
use std::sync::Arc;
let a = Arc::new(42);
let a2 = Arc::clone(&a);
// RefCell — 运行时借用检查
use std::cell::RefCell;
let c = RefCell::new(42);
*c.borrow_mut() = 100; // 可变借用
let v = *c.borrow(); // 不可变借用
// c.borrow_mut(); // panic! 已有不可变借用
// Cow — 写时复制
use std::borrow::Cow;
let mut cow = Cow::Borrowed("hello");
cow.to_mut().push_str(" world"); // 首次修改时分配智能指针对比
| Box | Rc | Arc | RefCell | |
|---|---|---|---|---|
| 所有权 | 单 | 单线程共享 | 跨线程共享 | 单+运行时借用 |
| 引用计数 | 否 | 是(非原子) | 是(原子) | 否 |
| 线程安全 | Send+Sync | !Send+!Sync | Send+Sync | Send+!Sync |
| 内部可变性 | 否 | 否(需RefCell) | 否(需Mutex) | 是(runtime) |
实践
力扣问题
力扣: 力扣单调栈 — Box + RefCell
AI 自检
Arc<T>的原子计数在 x86 上如何实现?LOCK CMPXCHG的具体开销是多少周期?Rc<T>的引用计数存在堆上还是栈上?释放时如何避免 double-free?