Box
Box
是 Rust 中一个非常方便的类型。当你使用Box
时,你可以把一个类型放在堆上而不是栈上。要创建一个新的 Box
,只需使用 Box::new()
并将元素放在里面即可。
fn just_takes_a_variable<T>(item: T) {} // Takes anything and drops it. fn main() { let my_number = 1; // This is an i32 just_takes_a_variable(my_number); just_takes_a_variable(my_number); // Using this function twice is no problem, because it's Copy let my_box = Box::new(1); // This is a Box<i32> just_takes_a_variable(my_box.clone()); // Without .clone() the second function would make an error just_takes_a_variable(my_box); // because Box is not Copy }
一开始很难想象在哪里使用它,但你在Rust中经常使用它。你记得&
是用于str
的,因为编译器不知道str
的大小:它可以是任何长度。但是&
的引用总是相同的长度,所以编译器可以使用它。Box
也是类似的。另外,你也可以在Box
上使用*
来获取值,就像使用&
一样。
fn main() { let my_box = Box::new(1); // This is a Box<i32> let an_integer = *my_box; // This is an i32 println!("{:?}", my_box); println!("{:?}", an_integer); }
这就是为什么Box被称为 "智能指针"的原因,因为它就像&
的引用(指针的一种),但可以做更多的事情。
你也可以使用Box来创建里面有相同结构的结构体。这些结构被称为递归,这意味着在Struct A里面也许是另一个Struct A,有时你可以使用Box来创建链表,尽管这在Rust中并不十分流行。但如果你想创建一个递归结构,你可以使用Box
。如果你试着不用 Box
会发生什么:
#![allow(unused)] fn main() { struct List { item: Option<List>, // ⚠️ } }
这个简单的List
有一项,可能是Some<List>
(另一个列表),也可能是None
。因为你可以选择None
,所以它不会永远递归。但是编译器还是不知道大小。
error[E0072]: recursive type `List` has infinite size
--> src\main.rs:16:1
|
16 | struct List {
| ^^^^^^^^^^^ recursive type has infinite size
17 | item: Option<List>,
| ------------------ recursive without indirection
|
= help: insert indirection (e.g., a `Box`, `Rc`, or `&`) at some point to make `List` representable
你可以看到,它甚至建议尝试Box
。所以我们用Box
把List包裹起来。
struct List { item: Option<Box<List>>, } fn main() {}
现在编译器用List
就可以了,因为所有的东西都在Box
后面,而且它知道Box
的大小。那么一个非常简单的列表可能是这样的:
struct List { item: Option<Box<List>>, } impl List { fn new() -> List { List { item: Some(Box::new(List { item: None })), } } } fn main() { let mut my_list = List::new(); }
即使没有数据也有点复杂,Rust并不怎么使用这种类型的模式。这是因为Rust对借用和所有权有严格的规定,你知道的。但如果你想启动一个这样的列表(链表),Box
可以帮助你。
Box
还可以让你在上面使用std::mem::drop
,因为它在堆上。这有时会很方便。