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 智能指针。