モジュールのレッスン (2/2)
スマート ポインター: Box および Rc
スマート ポインター は、ポインターのように動作するデータ構造ですが、メタデータと追加機能 (参照カウントや自動メモリ管理など) を備えています。
Rust では、通常の参照 (& など) はデータを借用するだけです。一方、スマート ポインターは、多くの場合、それらが指すデータを所有します。一般的な例には String や Vec<T> などがありますが、標準ライブラリには他の特殊なライブラリも用意されています。
ヒープ割り当て用のボックス<T>
最も単純なスマート ポインターは Box<T> で、データをスタックではなく ヒープに保存します。ヒープに割り当てられたデータへのポインタのみがスタック上に残ります。
主に以下のような場合に使用されます。
- コンパイル時にサイズがわからない型 (再帰型など) があり、正確なサイズが必要なコンテキストでその型の値を使用したい場合。
- 大量のデータをコピーせずに所有権を譲渡したい場合。
基本例:
fn main() {
let b = Box::new(5); // allocates the value 5 on the heap
println!("b = {}", *b); // dereferences with * to read the value
}再帰的なタイプと短所のリスト
再帰型は、定義の一部としてそれ自体の値を含めることができる型です。 Rust はコンパイル時に型の正確なサイズを知る必要があります。再帰型の深さは無限になる可能性があるため、Box (ポインターなのでサイズが固定されています) を使用しない限り、コンパイラーはエラーを生成します。
enum List {
Cons(i32, Box<List>),
Nil,
}Rc<T>: 参照カウント
1 つの値に複数の所有者が存在する場合があります。 Rc<T> (参照カウント) スマート ポインターは、値への参照数を追跡し、値がまだ使用されているかどうかを判断します。カウントがゼロに下がると、リソースは安全に割り当て解除されます。
[!NOTE]
Rc<T>は シングルスレッドのシナリオにのみ適しています。マルチスレッド (同時) シナリオの場合は、Arc<T>(アトミック参照カウント) を使用する必要があります。
use std::rc::Rc;
fn main() {
let a = Rc::new(5);
let b = Rc::clone(&a); // increments the reference count to 2
println!("References: {}", Rc::strong_count(&a));
}自分で試してみてください
演習 1: ボックスを使用したヒープ割り当て
Box::new を使用してヒープ上に整数値 42 を割り当て、それを val という名前の変数に割り当てます。次に、* 演算子を使用して明示的に参照を解除して、ボックスに格納されている値を出力します。
ヒントを表示
`let val = Box::new(42);` を使用して値をヒープに保存し、`println!` 内の `*val` で逆参照します。
3 回の試行後に解決策が利用可能になります
演習 2: 再帰的データ構造 (短所リスト)
タプル (i32, Box<List>) をラップする Cons とリストの終わりを表す Nil の 2 つのバリアントを含む List という名前の再帰列挙型を定義します。 main 関数で、要素 1 とそれに続く Nil を含むリンク リストをインスタンス化します。
ヒントを表示
enum は `enum List { Cons(i32, Box<List>), Nil }`として宣言されます。`List::Cons(1, Box::new(List::Nil))` でリストをインスタンス化します。
3 回の試行後に解決策が利用可能になります
演習 3: Rc と所有権を共有する
std::rc::Rc を使用して、変数 a に値 100 を含む共有整数を作成します。 Rc::clone を使用して、変数 b に参照のクローンを作成します。最後に、Rc::strong_count 関数を使用してアクティブな参照の数を出力します。
ヒントを表示
`Rc::new(100)`で値を作成します。`Rc::clone(&a)`でクローンを作成し、`Rc::strong_count(&a)`でオーナー数を表示します。
3 回の試行後に解決策が利用可能になります