導入
スマートポインタとは、「ただのポインタ」に追加の機能や保証を持たせた型のことです。実は第3章から使ってきた String や Vec もスマートポインタの一種でした。その最も単純な形が Box<T>——「値をヒープに置く」ためのいちばん素朴な箱です。
説明
Box<T> は、値をヒープに確保し、そのポインタをスタックで持ちます。
fn main() {
let b = Box::new(5); // 5 をヒープに置き、b はそれを指す
println!("b = {b}"); // 自動で中身が参照される → 5
} // b がスコープを抜けると、ヒープの 5 も自動解放
5 のような小さな値をわざわざヒープに置く意味は普段ありませんが、Box が本当に必要になるのは 「サイズがコンパイル時に決まらない型」 を扱うときです。代表例が、自分自身を含む再帰的なデータ構造です。
// 連結リスト:各要素が「次の要素」を持つ
enum List {
Cons(i32, Box<List>), // 値と「次への Box」
Nil, // 終端
}
use List::{Cons, Nil};
fn main() {
// 1 -> 2 -> 3 -> 終端
let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}
もし Cons(i32, List) と直接書くと、List の中に List が入り、その中にまた List……と、サイズが無限になってコンパイルできません。Box<List> にすると、中身はヒープに置かれ、構造体が持つのはサイズ固定のポインタだけになります。これで「入れ子の深さが実行時に決まる」構造をスタック上の固定サイズで表現できます。第3章の「可変サイズはヒープ+ポインタ」が、ここで効いてくるわけです。
graph LR
subgraph stack["スタック"]
b["b: Box(ptr ●)"]
end
subgraph heap["ヒープ"]
v["値 5"]
end
b -->|指す| v
Box は所有権のルールにそのまま従います。Box を持つ変数がその中身のヒープデータの所有者で、スコープを抜ければ自動解放。「所有者は1人」も守られます。
試すには
Box::new(値) は「値をヒープへ」の最も基本の道具です。再帰的な型(リスト・木構造)を Rust で作るときにまず登場します。「なぜ直接ではダメで Box が要るのか」——サイズが確定しないから、という理由を押さえておきましょう。