欢迎关注公众号 猩猩程序员
1.什么是 Box?
- Box 是“在堆上分配内存”的智能指针:值在堆上,栈上保存固定大小的指针。
- 典型使用场景:
- 递归类型(编译期大小无法确定)
- 动态分发的 trait 对象(Box)
- 将大对象放到堆上,降低移动时的栈拷贝成本
- 内存与语义:
- 实现 Drop:离开作用域自动释放堆内存
- 实现 Deref:可像引用一样解引用访问内部值
2.基础用法:Box::new 与解引用
示例 1:在堆上分配并使用
fn main() {
// 在堆上分配一个 i32 值 5
let b = Box::new(5);
// 自动解引用(Deref),像引用一样使用
println!("b = {}", b);
// 作用域结束时,Box 释放其堆上内存
}
3.递归类型:用 Box 打破无限大小
示例 2:链表(Cons List)
enum List {
Cons(i32, Box<List>),
Nil,
}
use List::{Cons, Nil};
fn main() {
// 构建: 1 -> 2 -> 3 -> Nil
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
// 递归位置使用 Box<List>(固定大小的指针),编译器可计算枚举大小
}
4.Trait 对象:Box 动态分发
示例 3:GUI 组件的 draw 行为
trait Draw {
fn draw(&self);
}
struct Button { label: String }
struct SelectBox { options: Vec<String> }
impl Draw for Button {
fn draw(&self) {
println!("绘制按钮: {}", self.label);
}
}
impl Draw for SelectBox {
fn draw(&self) {
println!("绘制选择框: {:?}", self.options);
}
}
fn main() {
// 不同具体类型,统一以 Box<dyn Draw> 保存
let components: Vec<Box<dyn Draw>> = vec![
Box::new(Button { label: "登录".into() }),
Box::new(SelectBox { options: vec!["是".into(), "否".into()] }),
];
for c in components {
c.draw(); // 动态分发
}
}
5.向下转型:Box::downcast
- 适用于“装箱任意类型,再尝试恢复为具体类型”的场景。
- 成功:Ok(Box);失败:Err(Box)。
示例 4:判定是否为 String
use std::any::Any;
fn print_if_string(value: Box<dyn Any>) {
if let Ok(string) = value.downcast::<String>() {
println!("是字符串: {}", string);
} else {
println!("不是字符串");
}
}
fn main() {
print_if_string(Box::new("Hello Rust".to_string()));
print_if_string(Box::new(42i32));
}
6.与 Pin 结合:Box::pin
- 构造 Pin<Box>;若 T: !Unpin,则值的内存位置被固定,不能再移动。
示例 5:固定一个不应被移动的类型
use std::pin::Pin;
struct NotUnpin {
data: String,
}
fn main() {
let pinned: Pin<Box<NotUnpin>> = Box::pin(NotUnpin { data: "fixed".into() });
// 若 NotUnpin 不实现 Unpin,则 pinned 中的数据位置保持不变
}
7.延迟初始化:Box::::new_uninit
- new_uninit 构造未初始化的 Box,需写入后再 assume_init 得到 Box。
- 必须确保确实完成了正确初始化,否则为未定义行为(unsafe)。
示例 6:延迟初始化
fn main() {
let mut boxed = Box::<u32>::new_uninit();
// 延迟初始化
boxed.write(5);
let boxed: Box<u32> = unsafe { boxed.assume_init() };
assert_eq!(*boxed, 5);
}
8.所有权、移动与克隆
- 所有权:Box 遵循所有权语义,move 后旧变量不可再用。
- 可变性:修改内部值需要可变借用(let mut 或 &mut)。
- 克隆:Box 的 Clone 会分配新内存并克隆内部值(T: Clone),相当于“深拷贝”;若需共享所有权请用 Rc/Arc(不在本节展开)。
欢迎关注公众号 猩猩程序员