その他

【Rust】所有権の移動について

その他
この記事は約2分で読めます。

はじめに

『Ownership』(所有権)の概念は、Rustの大きな特徴の一つです。

3つのルールを押さえておく必要があります。

  1. あらゆる値は変数に所有される
  2. オーナーがスコープから外れたら値も解放される
  3. オーナーは常に1人

簡単なI/Oの例

  • inputは値を所有するオーナー
  • 文字列はヒープメモリに(コンパイル時にサイズが不明なため)
  • main関数が終了した時、メモリも解放される
fn main() {
    // input: オーナー
    let mut input = String::new();

    // 入力を待ち受ける
    io::stdin().read_line(&mut input);
}

所有権の移動の例

  • inputは値を所有するオーナー
  • input2も同じ値を参照している

このままだとmain関数が終了した時、メモリの同一エリアを2度開放してしまうことになります。(エラー)

が、Rustはこのダブルフリーが起こらないよう、所有権が2つにならないよう勝手に移動させます。

fn main() {
    let mut input = String::new();
    // 所有権はinput2へ
    let mut input2 = input;

    // エラー: inputはもはや値への参照を持たない
    io::stdin().read_line(&mut input);
}

関数の引数でも同じことが起こります。

fn main() {
    let mut input = String::new();
    // 所有権はfunctionの引数strへ
    function(input);

    // エラー: inputはもはや値への参照を持たない
    io::stdin().read_line(&mut input);
}

fn function(str: String) {}

スタックの場合(コンパイル時にサイズが確定している)は参照ではなく値そのものをコピーできるので、移動は起こりません。

fn main() {
    let mut input = 3;
    let mut input2 = input;

    // 問題ない
    io::stdin().read_line(&mut input);
}

この所有権の移動に慣れないと、コンパイルが通らないのでプログラムを動かすことすらできません。