Box
Box
是 Rust 中非常方便的型別。當你使用 Box
時,你可以把型別放在堆積上而不是堆疊上。要做出新的 Box
,只要用 Box::new()
並把元素放在裡面即可。
fn just_takes_a_variable<T>(item: T) {} // 接受任何東西並丟棄. fn main() { let my_number = 1; // 這是 i32 just_takes_a_variable(my_number); just_takes_a_variable(my_number); // 使用這個函式兩次也沒問題, 因為它是 Copy let my_box = Box::new(1); // 這是 Box<i32> just_takes_a_variable(my_box.clone()); // 沒有 .clone() 時第二個函式會造成錯誤 just_takes_a_variable(my_box); // 因為 Box 不是 Copy }
一開始很難想像能在哪裡使用它,但你會在 Rust 中經常使用它。你記得 &
被用在 str
是因為編譯器不知道 str
的大小:它可以是任何長度。但是用 &
的參考永遠是相同的長度,所以編譯器可以使用它。Box
也類似。另外你也可以在 Box
上使用 *
來獲得值,就像使用 &
一樣:
fn main() { let my_box = Box::new(1); // 這是 Box<i32> let an_integer = *my_box; // 這是 i32 println!("{:?}", my_box); println!("{:?}", an_integer); }
這就是為什麼 Box 被稱為"智慧指標(smart pointer)"的原因,因為它就像 &
的參考(一種指標),但可以做更多的事情。
你也可以使用 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 並不怎麼常用這種類型的模式(pattern)。這是因為 Rust 如你所知的對借用(borrowing)和所有權(ownership)有嚴格的規定。但如果你想開始寫這樣的列表(連結串列)時,Box
能幫上忙。
Box
還可以讓你對它使用 std::mem::drop
,因為它放在堆積上。這有時候會很方便。