Box<T>
Box 是指向堆上数据的自有指针:
fn main() { let five = Box::new(5); println!("five: {}", *five); }
Box<T> 会实现 Deref<Target = T>,这意味着您可以直接在 Box<T> 上通过 T 调用相应方法。
递归数据类型或具有动态大小的数据类型需要使用 Box:
#[derive(Debug)] enum List<T> { /// A non-empty list: first element and the rest of the list. Element(T, Box<List<T>>), /// An empty list. Nil, } fn main() { let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil)))); println!("{list:?}"); }
-
Boxis likestd::unique_ptrin C++, except that it’s guaranteed to be not null. -
在以下情况下,
Box可能会很实用:- 在编译时间遇到无法知晓大小的类型,但 Rust 编译器需要知道确切大小。
- 想要转让大量数据的所有权。为避免在堆栈上复制大量数据,请改为将数据存储在
Box中的堆上,以便仅移动指针。
-
If
Boxwas not used and we attempted to embed aListdirectly into theList, the compiler would not be able to compute a fixed size for the struct in memory (theListwould be of infinite size). -
Box大小与一般指针相同,并且只会指向堆中的下一个List元素, 因此可以解决这个问题。 -
Remove the
Boxin the List definition and show the compiler error. We get the message “recursive without indirection”, because for data recursion, we have to use indirection, aBoxor reference of some kind, instead of storing the value directly.
探索更多
小众优化
Though Box looks like std::unique_ptr in C++, it cannot be empty/null. This makes Box one of the types that allow the compiler to optimize storage of some enums.
For example, Option<Box<T>> has the same size, as just Box<T>, because compiler uses NULL-value to discriminate variants instead of using explicit tag (“Null Pointer Optimization”):
use std::mem::size_of_val; struct Item(String); fn main() { let just_box: Box<Item> = Box::new(Item("Just box".into())); let optional_box: Option<Box<Item>> = Some(Box::new(Item("Optional box".into()))); let none: Option<Box<Item>> = None; assert_eq!(size_of_val(&just_box), size_of_val(&optional_box)); assert_eq!(size_of_val(&just_box), size_of_val(&none)); println!("Size of just_box: {}", size_of_val(&just_box)); println!("Size of optional_box: {}", size_of_val(&optional_box)); println!("Size of none: {}", size_of_val(&none)); }