🌊

参照による所有権の借用 (Rust)

2021/04/01に公開

参照による所有権の借用

  • 今回は、所有権をもらうことなく値を参照する方法についてまとめていきます。
  • 参照は、&演算子を使う事で行えます。

例:

struct Foo {
    x: i32,
}

fn main() {
    let foo = Foo { x: 13 };
    let f = &foo;
    println!("{}", f.x);
    // ①:ここで変数fはドロップします。
    // ②:ここで変数fooはドロップします。
}

[実行結果]

❯ cargo run   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)    Finished dev [unoptimized + debuginfo] target(s) in 1.55s     Running `target/debug/variables`
13
  • 下記にもう1つ例を示します。
  • こちらは、値の所有権をもらう代わりに引数として、オブジェクトへの参照を取る(s: &String)を引数にしていることが分かるcalculate_length関数を定義します。
  • この時、オブジェクトの参照の仕方としては下記のようになる。

1.png

  • また、関数の引数に参照を取ることを借用と言います。

例:

fn main() {
    let char1 = String::from("hello world");
    let len = calculate_length(&char1);

    println!("length: '{}' is {}.", char1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

[実行結果]

❯ cargo run
   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/variables`
length: 'hello world' is 11.

参照による所有権の可変な借用

  • Rustでは、所有者は、可変な借用の間は移動や変更ができません。
  • 下記の例では、関数の引数に参照を使用し、借用したものに対し変更をかけています。
  • この場合、実行結果のようにエラーとなります。

例:

fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}

[実行結果]

❯ cargo run
   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)
error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference
 --> src/main.rs:8:5
  |
7 | fn change(some_string: &String) {
  |                        ------- help: consider changing this to be a mutable reference: `&mut String`
8 |     some_string.push_str(", world");
  |     ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: could not compile `variables`

To learn more, run the command again with --verbose.
  • そこで、参照による所有権の可変に借用するために&mut演算子を使用します。
  • ただルールもあり、データ競合を防止するため、Rustでは同時に2つの変数から値を変更することはできません。

例:

struct Foo {
    x: i32,
}

fn do_something(f: Foo) {
    println!("{}", f.x);
    // ここで引数fはドロップします。
}

fn main() {
    let mut foo = Foo { x: 13 };
    let f = &mut foo;

    f.x = 13;
    // ここで変数fはドロップします。=> つまり、可変な借用もここでドロップします。

    println!("{}", foo.x);
    
    // 可変な借用はドロップされているため変更可能となります。
    foo.x = 7;
    
    // 変数fooの所有権を関数do_somethingに移動します。
    do_something(foo);
}

[実行結果]

❯ cargo run
   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/variables`
13
7

参照外し

  • &mutによる参照では、* 演算子によって参照を外すことで、所有者の値を設定することができます。

例:

fn main() {
    let mut foo = 26;
    let f = &mut foo;

    // 参照を外し、所有者の値を取得します。
    let bar = *f;

    // 参照を外し、参照の所有者の値を設定します。
    *f = 13;      
    println!("{}", bar);
    println!("{}", foo);
}

[実行結果]

❯ cargo run
   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.23s
     Running `target/debug/variables`
26
13

参照の参照

例:

struct Foo {
    x: i32,
}

fn do_something(a: &Foo) -> &i32 {
    return &a.x;
}

fn main() {
    let mut foo = Foo { x: 42 };
    let x = &mut foo.x;

    *x = 13;
    // 参照を外し、参照の所有者の値を設定します。
    // ここで変数xはドロップされます。
    // そのため、不変な参照が作成が可能になります。
    let y = do_something(&foo);
    println!("{}", y);
}

[実行結果]

❯ cargo run
   Compiling variables v0.1.0 (/Users/yoshitaka.koitabashi/Desktop/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/variables`
13

参考文献

Discussion