LoginSignup
1
0

More than 1 year has passed since last update.

Rust: 所有権について

Posted at

所有権システムについて

タングリングポインタを作り出さないための仕組み。
タングリングポインタとは解放済みのメモリ領域を指すポインタ(メモリの場所)のこと。
タングリングポインタのメモリ領域にアクセスすると未定義動作を起こす。
未定義動作を起こすことはバグやセキュリティリスクにつながるため、タングリングポインタを作ることは避けるべき。

所有権

Rustのすべての値は唯一の所有者がいる。
唯一の所有者がスコープを抜けると所有していた値は破棄され、プログラム内でアクセスできなくなる。

// {}はスコープを示すブロック
fn main() {
    {
	    let x = 34;
    } // xのスコープはここまで
    println!("x is {}", x); // 破棄されたxにアクセスしようとしている

上記を実行するとコンパイルエラーになる。

error[E0425]: cannot find value `x` in this scope
 --> src/main.rs:5:25
  |
5 |     println!("x is {}", x);
  |                         ^ not found in this scope

For more information about this error, try `rustc --explain E0425`.
error: could not compile `playground` due to previous error

内部に値を持つ構造体オブジェクトなどを扱うときは所有権の移行に注意する

構造体の中の構造体を扱うとき

// Fugaは内部にHoge型の構造体を持つ
struct Hoge {
    foo: [u8; 10],
    bar: [u8; 20],
}

struct Fuga {
    foo: Hoge,
    bar: Hoge,
}

impl Hoge {
    fn new() -> Hoge {
        Hoge {
            foo: [0; 10],
            bar: [1; 20],
        }
    }
}

impl Fuga {
    fn new() -> Fuga {
        Fuga {
            foo: Hoge::new(),
            bar: Hoge::new(),
        }
    }
}

fn main() {
    let fuga = Fuga::new();
    let _first_bar = fuga.bar; // fuga.barの所有権は_first_barに移った。
    println!(
        "first_foo: {:?}, first_bar: {:?}",
        fuga.foo.foo, fuga.bar.bar // fuga.barへはもうアクセスできない。
    );
}

上記のコンパイルエラー

error[E0382]: borrow of moved value: `fuga.bar`
  --> src/main.rs:36:9
   |
32 |     let _first_bar = fuga.bar; // fuga.barの所有権は_first_barに移った。
   |                      -------- value moved here
...
36 |         fuga.bar.bar // fuga.barへはもうアクセスできない。
   |         ^^^^^^^^^^^^ value borrowed here after move
   |
   = note: move occurs because `fuga.bar` has type `Hoge`, which does not implement the `Copy` trait
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

構造体を引数にとるメソッド

値をそのまま引数に渡すとメソッドの終わりで所有権が破棄される。
これを解決するためには借用を使う。

struct Sensor {
    latest: u8,
}

fn use_sensor(mut s: Sensor) {
    s.latest = 50;
    println!("{}", s.latest);
} // s のスコープはここまでで、渡されたs(sensor)の所有権は破棄される

fn main() {
    let sensor = Sensor { latest: 42 };
    use_sensor(sensor); // use_sensorで所有権が破棄されている
    println!("{}", sensor.latest); // sensorは所有権が破棄されているのでアクセスできない。コンパイルエラー
}

上記のコンパイルエラー

error[E0382]: borrow of moved value: `sensor`
  --> src/main.rs:13:20
   |
11 |     let sensor = Sensor { latest: 42 };
   |         ------ move occurs because `sensor` has type `Sensor`, which does not implement the `Copy` trait
12 |     use_sensor(sensor);
   |                ------ value moved here
13 |     println!("{}", sensor.latest);
   |                    ^^^^^^^^^^^^^ value borrowed here after move
   |
   = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

借用

所有権を動かす(ムーブ)ことなく値を渡す。
先のコンパイルエラーとなるコードは以下で解決できる

struct Sensor {
    latest: u8,
}

fn use_sensor(s: &mut Sensor) { // 引数は&<型名>とする、メソッド内で値の変更が発生する場合は左記のように&mut <型名>
    s.latest = 50;
    println!("{}", s.latest);
}

fn main() {
    let mut sensor = Sensor { latest: 42 };
    use_sensor(&mut sensor); // &を頭に付けることで参照渡しとする 値の変更がない場合は&sensor
    println!("{}", sensor.latest); // 正常に表示できる
}

出力結果

50
50

ライフタイム

値の所有者は、借用で貸した参照より長く生存する必要がある。

fn main() {
    let x;
    {
        let y = 50;
        x = &y; // xはyの値を借用している
    } // yのスコープはここまで
    println!("x: {}", x) // 破棄されたyを参照するxを使っているためエラーとなる
}

上記のコンパイルエラー

error[E0597]: `y` does not live long enough
 --> src/main.rs:5:13
  |
5 |         x = &y;
  |             ^^ borrowed value does not live long enough
6 |     }
  |     - `y` dropped here while still borrowed
7 |     println!("x: {}", x)
  |                       - borrow later used here

参照を含む構造体の場合

参照を含む構造体の定義にはライフタイムパラメータが必要

struct Image {
    raw: &[u8; 256], // 構造体が参照の値を持つとき
}

fn main() {
    let image;
    let a = [0; 256];
    image = Image { raw: &a };
    println!("{:?}", image.raw);
}

上記のコンパイルエラー

error[E0106]: missing lifetime specifier
  --> src/main.rs:13:10
   |
13 |     raw: &[u8; 256],
   |          ^ expected named lifetime parameter
   |
help: consider introducing a named lifetime parameter
   |
12 ~ struct Image<'a> {
13 ~     raw: &'a [u8; 256],
   |

上記の解決方法

struct Image<'a> { // ’aがライフタイムパラメータ。アポストロフィ`'`で始まる任意の小文字。
    raw: &'a [u8; 256],
}

fn main() {
    let image;
    let a = [0; 256];
    image = Image { raw: &a }; //
    println!("{:?}", image.raw);
}

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0