Rust Box 与智能指针:Deref 与 Drop
更新时间:2026-09-20。
Box<T>是最简单的智能指针:它像普通T一样拥有所有权,只是把数据放在堆上。理解它的Deref/Drop,就理解了 Rust 一整套智能指针。引用计数版见 Rc/RefCell,并发版见 Send/Sync。
一、Box:堆上的独占所有权
rust
let b = Box::new(5); // 5 分配在堆上,b 在栈上占一个指针
println!("{}", b); // 自动解引用,当普通 i32 用
// b 离开作用域:自动 drop,堆内存释放,无 GC三个典型使用场景:
- 编译期大小未知的类型:递归结构(链表/树节点),否则结构体大小无限递归无法布局。
- trait 对象:
Box<dyn Trait>装下运行期才确定的类型(动态分发见 trait 与泛型)。 - 转移大块数据:把大结构体放进 Box,传递时只移动指针,避免栈上拷贝。
rust
enum List {
Cons(i32, Box<List>), // Box 让递归有确定大小(一个指针)
Nil,
}
trait Draw { fn draw(&self); }
let objects: Vec<Box<dyn Draw>> = Vec::new();二、Deref:像用引用一样用 Box
rust
let x = 5;
let r = &x; // *r == 5
let b = Box::new(x);
assert_eq!(*b, 5); // Box 也能用 * 解引用,靠 Deref traitDeref/DerefMut 让智能指针在需要时自动转换:
rust
fn greet(name: &str) {}
let b = Box::new(String::from("a"));
greet(&b); // &Box<String> -> &String -> &str 连续解引用强制转换方法调用会自动逐层解引用:Box<String> 可直接 .as_str()、.len(),不必手写 (**b)。
三、Drop:确定性析构
rust
struct Guard;
impl Drop for Guard {
fn drop(&mut self) { println!("cleanup"); } // 离开作用域自动调用
}- 值离开作用域时自动、确定性地调用
drop(RAII,和 C++ 同源),不需要也不允许手动调drop()方法;要提前释放用标准库drop(x)(取走所有权)。 - 析构顺序:变量按声明逆序 drop;复合类型先 drop 字段再 drop 自身。
- 这也是 Rust 无 GC 却不泄漏内存的基础,可对照本站 C++ RAII 与内存布局。
四、常见智能指针选型
| 指针 | 所有权 | 线程 | 适用 |
|---|---|---|---|
Box<T> | 独占 | T: Send 即可 | 堆分配、递归类型、trait 对象 |
Rc<T> | 多所有者(计数) | 单线程(非 Send) | 图/树共享只读 |
Arc<T> | 多所有者(原子计数) | 多线程 | 跨线程共享 |
RefCell<T> | 借用检查移到运行时 | 单线程 | 内部可变性 |
Mutex<T> | 运行时加锁 | 多线程 | 跨线程内部可变性 |
五、常见坑
- 给所有东西都 Box:Box 意味着一次堆分配,小而固定大小的数据放栈上更快,别把它当默认。
Box::leak制造 'static:能临时把引用变'static,但内存有意不释放,只能用于程序生命周期数据,别在循环里调。- 递归类型忘记 Box:
Cons(i32, List)大小无限无法编译,用Box<List>截断。 - 手动调 drop 方法:不允许显式调用,会 double free,用
std::mem::drop提前释放。 - 自引用结构:让 Box 内数据指向自身字段极易因移动失效,新手用索引或重设计,深入再学
Pin。
总结
Box<T> 是独占所有权、把数据放在堆上、栈上仅留指针的最简智能指针,主要用于递归类型定大小、Box<dyn Trait> 装异构类型和低成本转移大块数据;它通过 Deref/DerefMut 支持 * 解引用、方法自动解引用与 &Box<T>→&T 的解引用强制转换,通过 Drop 在值离开作用域时按确定顺序自动释放(RAII、无 GC);选型上独占用 Box、单线程共享用 Rc、跨线程用 Arc、内部可变性单线程用 RefCell、多线程用 Mutex,并避免无谓堆分配、滥用 Box::leak 和手写自引用结构。