std::boxed::Box 教程(超级扩展版本)

Rust 的 std::boxed::Box<T> 类型是标准库 std::boxed 模块(以及相关 std::alloc 的分配支持)的核心组成部分,提供堆分配的智能指针,用于动态大小类型、递归结构和所有权转移的内存管理,支持 O(1) 分配/释放的单所有权堆盒。

1. std::boxed::Box 简介

  • 导入和高级结构:除了基本导入 use std::boxed::Box;,高级用法可包括 use std::alloc::Layout; 以自定义布局、use std::ptr::NonNull; 以 raw 指针和 use std::pin::Pin; 以固定 Box。模块的内部结构包括 Box 的 NonNull 指针(分配 + 布局)和 Deref 的 fat pointer 支持 ?Sized T(如 trait 对象 vtable)。
    • 类型详解
      • Box<T, A: Allocator = Global>:堆指针,支持 new/leak/into_raw/from_raw/allocate/layout_for_value/new_uninit/new_zeroed/pin 等;泛型 A 以自定义分配;支持 ?Sized T 如 Box
      • Box<[T]>/Box<str>:切片/字符串特化,支持 into_boxed_slice/into_boxed_str。
      • Pin<Box<T>>:固定 Box,防 move,支持 new/unbox。
    • 函数和方法扩展Box::new 创建、Box::new_in 自定义 A、Box::allocate Layout 分配、Box::from_raw_in 恢复、Box::leak 'static &mut T、Box::pin_in Pin 创建。
    • :无,但相关如 box! (syntax) proposal。
  • 设计哲学扩展std::boxed::Box 遵循 "owned heap pointer",通过 Drop 自动 dealloc;零成本 deref;unsafe 方法允许低级但需 layout/align;?Sized 支持 dst 如 slice/trait。Box 是 Send + Sync 如果 T 是,允许线程转移;无内置 shared (用 Arc)。
  • 跨平台详解:分配用 malloc (Unix)/HeapAlloc (Windows);对齐 T align_of;测试差异用 CI,焦点大 Box 分配失败于低内存 OS 和 fat pointer 大小 (vtable)。
  • 性能详析:new O(1) 分配 ~50-200ns;deref 零;leak O(1) 无 dealloc;大 T memcpy 慢。基准用 criterion,profile 用 heaptrack 分配高峰。
  • 常见用例扩展:递归结构(链表/tree)、trait 对象(动态分发)、堆逃逸(闭包捕获)、游戏对象分配、测试 mock 堆。
  • 超级扩展概念:与 std::alloc::Layout 集成自定义对齐;与 std::panic::catch_unwind 安全 dealloc 大 Box;错误 panic 于 OOM;与 thin-box::ThinBox 薄 trait 对象替代;高性能用 box_alloc no_std Box;与 tracing::field Box 日志;历史:从 1.0 Box 到 1.36 alloc trait 优化。

2. 创建 Box:Box::new 和 new_in

Box::new 是入口,new_in 自定义分配。

示例:基本 Box 创建(值和 dst 扩展)

use std::boxed::Box;

fn main() {
    let b = Box::new(42);
    println!("值: {}", *b);  // deref

    let slice: Box<[i32]> = Box::from([1, 2, 3]);
    println!("slice: {:?}", slice);
}
  • 解释new 分配 T。from 从数组/切片。性能:小 T 快。

示例:NewIn Custom Alloc(分配器扩展)

use std::boxed::Box;
use std::alloc::Global;

fn main() {
    let b = Box::new_in(42, Global);
}
  • 解释new_in 用 A。扩展:用 jemalloc 全局优化大 Box。

示例:NewUninit 和 Zeroed(未初始化扩展)

use std::boxed::Box;

fn main() {
    let mut b_uninit = Box::<i32>::new_uninit();
    unsafe { b_uninit.as_mut_ptr().write(42); }
    let b = unsafe { b_uninit.assume_init() };
    println!("init: {}", *b);

    let b_zero = Box::<[u8; 1024]>::new_zeroed();
    let zero_slice = unsafe { b_zero.assume_init() };
    println!("zero: all zero? {}", zero_slice.iter().all(|&x| x == 0));
}
  • 解释new_uninit 未初始化分配。assume_init unsafe 假设 init。new_zeroed 零填充。陷阱:读未 init UB。

示例:Box Dyn Trait(dst 扩展)

use std::boxed::Box;

trait Trait {
    fn method(&self);
}

struct Impl(i32);

impl Trait for Impl {
    fn method(&self) {
        println!("val: {}", self.0);
    }
}

fn main() {
    let boxed: Box<dyn Trait> = Box::new(Impl(42));
    boxed.method();
}
  • 解释Box<dyn Trait> fat pointer (ptr + vtable)。扩展:use Any downcast。

3. 操作 Box:Deref、Leak、IntoRaw

操作访问和转换。

示例:Deref 和 DerefMut(透明访问扩展)

use std::boxed::Box;

fn main() {
    let mut b = Box::new(vec![1, 2]);
    b.push(3);  // deref mut
    println!("len: {}", b.len());
}
  • 解释:Deref 到 &Vec。性能:零开销。

示例:Leak 'static(全局扩展)

use std::boxed::Box;

fn main() {
    let leaked = Box::leak(Box::new(42));
    println!("leak: {}", leaked);  // &'static i32
}
  • 解释leak 返回 &'static mut T,忘记 dealloc。扩展:用 static mut 全局。

示例:IntoRaw 和 FromRaw(手动管理扩展)

use std::boxed::Box;

fn main() {
    let b = Box::new(42);
    let raw = Box::into_raw(b);
    unsafe { println!("raw: {}", *raw); }
    let b_back = unsafe { Box::from_raw(raw) };
}
  • 解释into_raw 释放为 *mut T。from_raw 恢复。unsafe 管理所有权。

示例:Pin Box(固定扩展)

use std::boxed::Box;
use std::pin::Pin;

fn main() {
    let pinned = Box::pin(42);
    let mut_pinned = pinned.as_mut();
    *mut_pinned = 43;
}
  • 解释pin 返回 Pin<Box>。as_mut mut 访问不动点。扩展:用于 self-ref 或 poll。

4. 高级:Box Slice、Str、Trait Obj

  • Slice:动态大小。

示例:Box Slice(数组扩展)

use std::boxed::Box;

fn main() {
    let boxed_slice: Box<[i32]> = Box::from(vec![1, 2, 3]);
    println!("slice: {:?}", boxed_slice);

    let boxed_array = Box::new([1, 2, 3]);
    let slice_from_array = &*boxed_array as &[i32];
}
  • 解释from Vec 到 Box<[T]>。扩展:use into_boxed_slice 原子。

示例:Box Str(字符串扩展)

use std::boxed::Box;

fn main() {
    let boxed_str: Box<str> = Box::from("hello");
    println!("str: {}", boxed_str);
}
  • 解释from &str/String 到 Box。扩展:use into_boxed_str 原子。

示例:Box Dyn Send+Sync(线程trait扩展)

use std::boxed::Box;
use std::thread;

fn main() {
    let boxed_trait: Box<dyn Send + Sync + Fn()> = Box::new(|| println!("closure"));
    thread::spawn(move || (boxed_trait)()).join().unwrap();
}
  • 解释:Box<dyn Trait + Send + Sync> 线程安全。扩展:use vtable 检查 trait bound。

5. 错误和panic:Box

Box panic 于分配失败。

示例:Alloc Error(OOM 扩展)

use std::boxed::Box;

fn main() {
    // Box::new 大 T OOM panic
    // 用 alloc trait try
    // future try_new
}
  • 解释:分配失败 panic "out of memory"。扩展:use fallible-alloc crate try。

6. 高级主题:Unsafe Raw、Pin 和 集成

  • Unsafe:低级。

示例:Unsafe New(布局扩展)

use std::boxed::Box;
use std::alloc::Layout;

fn main() {
    let layout = Layout::new::<[i32; 5]>();
    let ptr = unsafe { std::alloc::alloc(layout) as *mut [i32; 5] };
    let b = unsafe { Box::from_raw(ptr) };
}
  • 解释:手动布局分配。unsafe 管理。

7. 最佳实践和常见陷阱

  • Box 最佳:用 recursion 链表;trait obj 动态;leak 全局。
  • 性能:小 T 栈大 T 堆;pin 防 move。
  • 错误:panic OOM,用 try alloc。
  • 安全:unsafe from_raw 需 valid ptr。
  • 跨平台:alloc 一致。
  • 测试:miri UB;fuzz alloc。
  • 资源:drop dealloc;leak 永久。
  • 常见扩展
    • OOM:try alloc 处理。
    • 未 init:new_uninit 安全。
    • Fat ptr:trait vtable 大小。
    • Move pin:Pin 防。

8. 练习建议

  1. 编写递归树:Box 子。
  2. 实现 trait 工厂:Box 返回。
  3. 创建 uninit 缓冲:new_uninit 填充。
  4. 处理 OOM:模拟 alloc fail 测试恢复。
  5. 基准:比较 Box vs Arc alloc 时间,用 criterion。
  6. 与 pin:用 Pin<Box> poll。
  7. 错误框架:mock raw ptr 测试 from_raw 恢复。
  8. 高级 app:实现 VM 堆:Box<[u8]> 内存块。