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,因为它在堆上。这有时会很方便。