智能指针的内存管理原理

原理

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_subLOCK 前缀(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"); // 首次修改时分配

智能指针对比

BoxRcArcRefCell
所有权单线程共享跨线程共享单+运行时借用
引用计数是(非原子)是(原子)
线程安全Send+Sync!Send+!SyncSend+SyncSend+!Sync
内部可变性否(需RefCell)否(需Mutex)是(runtime)

实践

力扣问题

力扣: 力扣单调栈 — Box + RefCell

AI 自检

  1. Arc<T> 的原子计数在 x86 上如何实现?LOCK CMPXCHG 的具体开销是多少周期?
  2. Rc<T> 的引用计数存在堆上还是栈上?释放时如何避免 double-free?