跳到主要内容

Box 智能指针

Box<T> 是 Rust 中最简单的智能指针,它在堆上分配内存并拥有数据的所有权。Box 提供了一种将数据从栈移动到堆的方式。

什么是 Box?

Box<T> 是一个智能指针,它:

  • 在堆上分配内存
  • 拥有数据的所有权
  • 实现了 Deref 和 Drop trait
  • 大小固定(只是一个指针)

基本用法

创建 Box

fn main() {
// 在堆上分配一个整数
let boxed_int = Box::new(42);
println!("堆上的值: {}", boxed_int);

// 在堆上分配一个字符串
let boxed_string = Box::new(String::from("Hello, Box!"));
println!("堆上的字符串: {}", boxed_string);

// 在堆上分配一个向量
let boxed_vec = Box::new(vec![1, 2, 3, 4, 5]);
println!("堆上的向量: {:?}", boxed_vec);
}

解引用 Box

fn main() {
let boxed_value = Box::new(100);

// 自动解引用
println!("值: {}", boxed_value);

// 显式解引用
println!("显式解引用: {}", *boxed_value);

// 获取引用
let reference = &*boxed_value;
println!("引用: {}", reference);
}

使用场景

1. 大型数据结构

// 大型结构体
#[derive(Debug)]
struct LargeStruct {
data: [u8; 1024 * 1024], // 1MB 数据
id: u32,
}

fn main() {
// 避免栈溢出,将大型数据放在堆上
let large_data = Box::new(LargeStruct {
data: [0; 1024 * 1024],
id: 1,
});

println!("大型数据 ID: {}", large_data.id);

// 传递 Box 只是移动指针,不复制数据
process_large_data(large_data);
}

fn process_large_data(data: Box<LargeStruct>) {
println!("处理大型数据,ID: {}", data.id);
// data 在函数结束时自动释放
}

2. 递归数据结构

// 链表节点
#[derive(Debug)]
enum List {
Cons(i32, Box<List>),
Nil,
}

impl List {
fn new() -> List {
List::Nil
}

fn prepend(self, elem: i32) -> List {
List::Cons(elem, Box::new(self))
}

fn len(&self) -> usize {
match self {
List::Cons(_, tail) => 1 + tail.len(),
List::Nil => 0,
}
}

fn stringify(&self) -> String {
match self {
List::Cons(head, tail) => {
format!("{}, {}", head, tail.stringify())
}
List::Nil => {
format!("Nil")
}
}
}
}

fn main() {
let mut list = List::new();

list = list.prepend(1);
list = list.prepend(2);
list = list.prepend(3);

println!("链表: {}", list.stringify());
println!("长度: {}", list.len());
}

3. 二叉树

#[derive(Debug)]
struct TreeNode {
value: i32,
left: Option<Box<TreeNode>>,
right: Option<Box<TreeNode>>,
}

impl TreeNode {
fn new(value: i32) -> Self {
TreeNode {
value,
left: None,
right: None,
}
}

fn insert(&mut self, value: i32) {
if value < self.value {
match self.left {
Some(ref mut left) => left.insert(value),
None => self.left = Some(Box::new(TreeNode::new(value))),
}
} else {
match self.right {
Some(ref mut right) => right.insert(value),
None => self.right = Some(Box::new(TreeNode::new(value))),
}
}
}

fn search(&self, value: i32) -> bool {
if value == self.value {
true
} else if value < self.value {
match self.left {
Some(ref left) => left.search(value),
None => false,
}
} else {
match self.right {
Some(ref right) => right.search(value),
None => false,
}
}
}

fn inorder_traversal(&self) -> Vec<i32> {
let mut result = Vec::new();

if let Some(ref left) = self.left {
result.extend(left.inorder_traversal());
}

result.push(self.value);

if let Some(ref right) = self.right {
result.extend(right.inorder_traversal());
}

result
}
}

fn main() {
let mut root = TreeNode::new(10);

root.insert(5);
root.insert(15);
root.insert(3);
root.insert(7);
root.insert(12);
root.insert(18);

println!("树结构: {:#?}", root);
println!("中序遍历: {:?}", root.inorder_traversal());
println!("搜索 7: {}", root.search(7));
println!("搜索 20: {}", root.search(20));
}

性能考虑

堆分配开销

use std::time::Instant;

fn main() {
let n = 1_000_000;

// 栈分配(实际上这会导致栈溢出)
let start = Instant::now();
// let stack_data = [0; 1_000_000]; // 这会栈溢出
// println!("栈分配时间: {:?}", start.elapsed());

// 堆分配
let start = Instant::now();
let heap_data = Box::new([0; 1_000_000]);
println!("堆分配时间: {:?}", start.elapsed());

// 访问数据
let start = Instant::now();
let sum: i32 = heap_data.iter().sum();
println!("访问时间: {:?}, 和: {}", start.elapsed(), sum);
}

Box vs Vec 性能比较

use std::time::Instant;

fn main() {
let n = 1_000_000;

// 使用 Box<[i32]>
let start = Instant::now();
let boxed_array = Box::new([0; 1_000_000]);
println!("Box 分配时间: {:?}", start.elapsed());

// 使用 Vec<i32>
let start = Instant::now();
let vec_data = vec![0; n];
println!("Vec 分配时间: {:?}", start.elapsed());

// 内存使用比较
println!("Box 大小: {} bytes", std::mem::size_of_val(&boxed_array));
println!("Vec 大小: {} bytes", std::mem::size_of_val(&vec_data));
}

Box 的方法

常用方法

fn main() {
let boxed_value = Box::new(42);

// into_raw - 获取原始指针
let raw_ptr = Box::into_raw(boxed_value);
println!("原始指针: {:p}", raw_ptr);

// from_raw - 从原始指针重建 Box(不安全)
let restored_box = unsafe { Box::from_raw(raw_ptr) };
println!("恢复的值: {}", restored_box);

// leak - 泄漏 Box,返回静态引用
let leaked_ref: &'static mut i32 = Box::leak(Box::new(100));
*leaked_ref = 200;
println!("泄漏的引用: {}", leaked_ref);
}

Box 和切片

fn main() {
// 从向量创建 Box<[T]>
let vec = vec![1, 2, 3, 4, 5];
let boxed_slice: Box<[i32]> = vec.into_boxed_slice();
println!("装箱切片: {:?}", boxed_slice);

// 从数组创建 Box<[T]>
let array = [1, 2, 3, 4, 5];
let boxed_array: Box<[i32]> = Box::new(array);
println!("装箱数组: {:?}", boxed_array);

// 创建未初始化的 Box
let boxed_uninit: Box<[i32]> = vec![0; 5].into_boxed_slice();
println!("未初始化装箱: {:?}", boxed_uninit);
}

实际应用示例

状态机

trait State {
fn handle(self: Box<Self>) -> Box<dyn State>;
fn name(&self) -> &str;
}

struct IdleState;
struct ProcessingState;
struct CompleteState;

impl State for IdleState {
fn handle(self: Box<Self>) -> Box<dyn State> {
println!("从空闲状态转换到处理状态");
Box::new(ProcessingState)
}

fn name(&self) -> &str {
"Idle"
}
}

impl State for ProcessingState {
fn handle(self: Box<Self>) -> Box<dyn State> {
println!("从处理状态转换到完成状态");
Box::new(CompleteState)
}

fn name(&self) -> &str {
"Processing"
}
}

impl State for CompleteState {
fn handle(self: Box<Self>) -> Box<dyn State> {
println!("从完成状态转换到空闲状态");
Box::new(IdleState)
}

fn name(&self) -> &str {
"Complete"
}
}

struct StateMachine {
state: Box<dyn State>,
}

impl StateMachine {
fn new() -> Self {
StateMachine {
state: Box::new(IdleState),
}
}

fn transition(&mut self) {
println!("当前状态: {}", self.state.name());
let old_state = std::mem::replace(&mut self.state, Box::new(IdleState));
self.state = old_state.handle();
}
}

fn main() {
let mut machine = StateMachine::new();

for _ in 0..5 {
machine.transition();
}
}

插件系统

trait Plugin {
fn name(&self) -> &str;
fn execute(&self, input: &str) -> String;
}

struct UppercasePlugin;
struct ReversePlugin;
struct LengthPlugin;

impl Plugin for UppercasePlugin {
fn name(&self) -> &str {
"Uppercase"
}

fn execute(&self, input: &str) -> String {
input.to_uppercase()
}
}

impl Plugin for ReversePlugin {
fn name(&self) -> &str {
"Reverse"
}

fn execute(&self, input: &str) -> String {
input.chars().rev().collect()
}
}

impl Plugin for LengthPlugin {
fn name(&self) -> &str {
"Length"
}

fn execute(&self, input: &str) -> String {
format!("Length: {}", input.len())
}
}

struct PluginManager {
plugins: Vec<Box<dyn Plugin>>,
}

impl PluginManager {
fn new() -> Self {
PluginManager {
plugins: Vec::new(),
}
}

fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.push(plugin);
}

fn execute_all(&self, input: &str) {
for plugin in &self.plugins {
let result = plugin.execute(input);
println!("{}: {}", plugin.name(), result);
}
}
}

fn main() {
let mut manager = PluginManager::new();

manager.register_plugin(Box::new(UppercasePlugin));
manager.register_plugin(Box::new(ReversePlugin));
manager.register_plugin(Box::new(LengthPlugin));

manager.execute_all("Hello, World!");
}

最佳实践

1. 何时使用 Box

// 好的使用场景:
// - 递归数据结构
// - 大型数据避免栈溢出
// - trait 对象
// - 已知大小但在编译时未知的类型

// 避免的场景:
// - 小型数据(直接在栈上更高效)
// - 需要共享所有权(使用 Rc/Arc)
// - 需要内部可变性(使用 RefCell)

2. 内存管理

fn demonstrate_memory_management() {
{
let boxed_data = Box::new(vec![1, 2, 3, 4, 5]);
println!("数据: {:?}", boxed_data);
// boxed_data 在作用域结束时自动释放
}

// 手动控制释放时机
let boxed_value = Box::new(42);
drop(boxed_value); // 显式释放
// println!("{}", boxed_value); // 编译错误!
}

3. 与其他智能指针的选择

use std::rc::Rc;
use std::sync::Arc;

fn choosing_smart_pointers() {
// Box: 单一所有权,堆分配
let _box_data = Box::new(42);

// Rc: 多个所有者,单线程
let _rc_data = Rc::new(42);

// Arc: 多个所有者,多线程
let _arc_data = Arc::new(42);

println!("选择合适的智能指针很重要");
}

Box 是 Rust 中最基础的智能指针,理解它的使用场景和特性对于掌握 Rust 的内存管理至关重要。下一节我们将学习 Rc 智能指针。