Box<T>
Box は、ヒープ上のデータを指す所有ポインタ です:
// Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 fn main() { let five = Box::new(5); println!("five: {}", *five); }
Box<T> は Deref<Target = T> を実装しているため、 T のメソッドを Box<T> に対して直接呼び出せます。
再帰的なデータ型や動的なサイズを持つデータ型は、ポインタによる間接参照なしでは インラインに格納できません。Box はその間接参照を実現します:
// Copyright 2023 Google LLC // SPDX-License-Identifier: Apache-2.0 #[derive(Debug)] enum List<T> { /// 空でないリスト: 最初の要素と残りのリスト。 Element(T, Box<List<T>>), /// 空のリスト。 Nil, } fn main() { let list: List<i32> = List::Element(1, Box::new(List::Element(2, Box::new(List::Nil)))); println!("{list:?}"); }
-
Boxは C++ のstd::unique_ptrに似ていますが、null ではないことが 保証されています。 -
次のような場合、
Boxが役立ちます:- コンパイル時にサイズを知ることができない型を扱うが、Rust コンパイラは 正確なサイズを知る必要がある場合。
- 大量のデータの所有権を移動したい場合。スタック上で大量のデータをコピーする ことを避けるため、代わりにデータをヒープ上の
Boxに格納し、 移動するのはポインタだけにします。
-
Boxを使わず、Listの中にListを直接埋め込もうとすると、 コンパイラはメモリ上のその型の固定サイズを計算できません (Listのサイズが無限になってしまうためです)。 -
Boxは通常のポインタと同じサイズであり、 ヒープ上のListの次の要素を指すだけなので、この問題を解決します。 -
Listの定義からBoxを取り除き、コンパイラエラーを確認してください。すると “recursive without indirection” というメッセージが表示されます。これは、データの再帰では 値を直接格納するのではなく、Boxや何らかの参照のような間接参照を使う 必要があるためです。 -
Boxは C++ のstd::unique_ptrに似ていますが、空や null にはできません。 そのためBoxは、コンパイラが一部の enum の格納を最適化できる型の 1 つになります(“niche optimization”)。