これはRustのスマートポインタについての解説を読んで、記憶に残したいところをかいつまんだメモです
特別なことは何も書いていないので、この記事を読むのではなく、Rustの公式マニュアルページを読んでください
背景
Rust By ExampleでBoxを使ったリストの例が出てきたため、それにともない、調べました
スマートポインタ
参照は&記号で示唆され、指している値を借用します。データを参照すること以外に特別な能力は何もありません。 また、オーバーヘッドもなく、ポインタの中では最も頻繁に使われます。
一方、スマートポインタは、ポインタのように振る舞うだけでなく、追加のメタデータと能力があるデータ構造です。
所有権と借用の概念を使うRustにおいて、参照とスマートポインタにはもう1つ違いがあります。参照はデータを借用するだけのポインタなのです。 対照的に多くの場合、スマートポインタは指しているデータを所有します。
ヒープのデータを指すBoxを使用する
ボックスにより、スタックではなくヒープにデータを格納することができます。スタックに残るのは、 ヒープデータへのポインタです。
fn main() {
let b = Box::new(5);
println!("b = {}", b);
}
変数bを定義してBoxの値を保持します。Boxは値5を指し、値5はヒープに確保されています。
所有された値と全く同じでスコープを抜けるとき、実際bはmainの終わりで抜けるのですが、 ボックスはメモリから解放されます。メモリの解放は(スタックに格納されている)ボックスと(ヒープに格納されている)指しているデータに対して起きます。
コンパイル時にコンパイラが知っておかねばならないのは、ある型が占有する領域の大きさです。コンパイル時にサイズがわからない型の1つ として 再帰的な型があります。この型の値は、値の一部として同じ型の他の値を持つ場合があります。値のこうしたネストは、理論的には無限に続く可能性があるので、コンパイラは再帰的な型の値が必要とする領域を知ることができないのです。
実装
続いて、Boxを実際に使ったコンスリストを実装していきます
この、データと次のデータへのポインタを持っているやつのこと、コンスリストっていうんですね
以下はコンパイルに失敗するやつ
enum List {
Cons(i32, List),
Nil,
}
error[E0072]: recursive type `List` has infinite size
--> src\main.rs:1:1
|
1 | enum List {
| ^^^^^^^^^
2 | Cons(i32, List),
| ---- recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
|
2 | Cons(i32, Box<List>),
| ++++ +
For more information about this error, try `rustc --explain E0072`.
Rustはかしこいので、Boxを使うように示唆してくれています
以下の通りで、(unused valueでwarningは出ますが)コンパイルできます
enum List {
Cons(i32, Box<List>),
Nil,
}
fn main() {
use List::*;
println!("Hello, world!");
let list : List = Cons(1, Box::new(Cons(
2, Box::new(Cons(
3, Box::new(Nil))))));
}