blog.dopana

Back

Rust入門シリーズの第5回です。所有権はRustを最も特徴づける考え方であり、ガベージコレクタなしでメモリ安全になる理由です。

ルール#

  1. Rustの各値には1つの所有者がいる。
  2. 同時に所有者は1つだけ。
  3. 所有者がスコープを外れると値はドロップされる。
fn main() {
    let s = String::from("hello");
    println!("{s}");
}   // sがスコープを外れる; メモリが解放される
rust

ムーブセマンティクス#

値を別の変数に代入すると所有権が移動し、古い束縛は使えなくなります:

let s1 = String::from("hello");
let s2 = s1;
// println!("{s1}");   // エラー: 値が移動した
println!("{s2}");      // 成功
rust

文字列はコピーされません。データと所有権がs2に移動します。だからRustには解放後使用(use-after-free)バグがありません。

Copy型#

コピーが安価な小さな型は、移動ではなくコピーされます。これらはCopyトレイトを実装しています:

let a = 5;
let b = a;      // aは引き続き使用可能
rust

整数、浮動小数点数、ブール、charCopyです。StringVecは違います。

Clone#

ヒープに割り当てられた値をコピーするには、明示的に.clone()を呼びます:

let s1 = String::from("hello");
let s2 = s1.clone();
println!("{s1} {s2}");   // 両方有効
rust

cloneはディープコピーを作ります。明示的な呼び出しでコストが見えます。

所有権と関数#

値を関数に渡すと移動し、返すとまた移動します:

fn take_and_return(s: String) -> String {
    s
}

fn main() {
    let s = String::from("hi");
    let s = take_and_return(s);   // 移動され、また返される
}
rust

所有権とスコープ#

値は所有者のスコープが終わるとドロップされます。スコープベースのクリーンアップにより、メモリだけでなくリソースも自動的に解放されます。

なぜ重要か#

所有権により、コンパイラは解放後使用・二重解放・ダングリングポインタが存在しないことを証明できます — CやC++を悩ませるバグです。普通のコードを書くだけで、コンパイラが安全性を強制します。

結論#

所有権: 値ごとに1人の所有者、デフォルトでムーブ、Copy型はコピー、深いコピーには明示的なclone。シリーズ次回は参照とスライスです。

参考文献#