はじめに
この実験では、Rust において変数がリソースの所有権を持ち、所有者は 1 つだけであることが説明されます。これにより、リソースが複数回解放されることが防止されます。変数が代入される場合や関数の引数が値渡しで渡される場合、リソースの所有権が移されます。これをムーブと呼びます。ムーブの後、以前の所有者はもはや使用できなくなり、ダングリングポインターが作成されるのを避けるためです。コード例は、スタック上に割り当てられた変数とヒープ上に割り当てられた変数の所有権がどのように移されるか、および所有権が移された後に変数にアクセスするとどのようにエラーが発生するかを示すことで、これらの概念を示しています。
注: 実験でファイル名が指定されていない場合、好きなファイル名を使用できます。たとえば、
main.rsを使用して、rustc main.rs &&./mainでコンパイルして実行できます。
所有権とムーブ
変数が自分自身のリソースの解放を担当しているため、リソースは所有者を 1 人だけにすることができます。これにより、リソースが 2 回以上解放されることも防止されます。ただし、すべての変数がリソースを所有しているわけではありません(たとえば、[参照])。
代入 (let x = y) や関数の引数を値渡し (foo(x)) を行う場合、リソースの 所有権 が移されます。Rust の用語では、これを ムーブ と呼びます。
リソースを移動させた後、以前の所有者はもはや使用できなくなります。これにより、ダングリングポインターが作成されるのが回避されます。
// この関数は、ヒープ上に割り当てられたメモリの所有権を取得します
fn destroy_box(c: Box<i32>) {
println!("Destroying a box that contains {}", c);
// `c` が破棄され、メモリが解放されます
}
fn main() {
// _スタック_ 上に割り当てられた整数
let x = 5u32;
// `x` を *コピー* して `y` に代入 - リソースは移動しません
let y = x;
// 両方の値を独立して使用できます
println!("x is {}, and y is {}", x, y);
// `a` は、ヒープ上に割り当てられた整数へのポインターです
let a = Box::new(5i32);
println!("a contains: {}", a);
// `a` を *ムーブ* して `b` に代入
let b = a;
// `a` のポインターアドレスがコピーされ(データではなく)、`b` に代入されます。
// 両方は現在、同じヒープ上に割り当てられたデータへのポインターですが、
// 現在は `b` がそれを所有しています。
// エラー!`a` はもはやデータにアクセスできなくなりました。なぜなら、
// ヒープメモリを所有していなくなったからです
//println!("a contains: {}", a);
// TODO ^ この行のコメントを外してみてください
// この関数は、`b` からヒープ上に割り当てられたメモリの所有権を取得します
destroy_box(b);
// この時点でヒープメモリが解放されているため、この操作は
// 解放されたメモリを参照することになりますが、コンパイラによって禁止されています
// エラー!前のエラーと同じ理由です
//println!("b contains: {}", b);
// TODO ^ この行のコメントを外してみてください
}
まとめ
おめでとうございます!あなたは所有権とムーブの実験を完了しました。あなたの技術を向上させるために、LabEx でさらに多くの実験を行って練習することができます。