Box
Box 是指向堆上数据的自有指针:
fn main() {
let five = Box::new(5);
println!("five: {}", *five);
}
Box<T>
会实现 Deref<Target = T>
,这意味着您可以直接在 Box
递归数据类型或具有动态大小的数据类型需要使用 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:?}");
}
This slide should take about 8 minutes.
Box
is likestd::unique_ptr
in C++, except that it’s guaranteed to be not null.在以下情况下,
Box
可能会很实用:- 在编译时间遇到无法知晓大小的类型,但 Rust 编译器需要知道确切大小。
- 想要转让大量数据的所有权。为避免在堆栈上复制大量数据,请改为将数据存储在
Box
中的堆上,以便仅移动指针。
If
Box
was not used and we attempted to embed aList
directly into theList
, the compiler would not be able to compute a fixed size for the struct in memory (theList
would be of infinite size).Box
大小与一般指针相同,并且只会指向堆中的下一个List
元素, 因此可以解决这个问题。Remove the
Box
in the List definition and show the compiler error. We get the message “recursive without indirection”, because for data recursion, we have to use indirection, aBox
or 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));
}