Box<T>

Box は、ヒープ上のデータを指す所有ポインタ です:

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let five = Box::new(5);
    println!("five: {}", *five);
}
5StackHeapfive

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:?}");
}
StackHeaplistElement1Element2Nil
  • 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”)。