北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

Rustの&mutのmoveとreborrow

&mutCopy trait を 実装していない 。よって、こちらは実行できない。

fn main() {
    let x: &mut i32 = &mut 0;
    {
        let y = x;
        println!("y: {}", y);
    }
    println!("x: {}", x);
}
   Compiling playground v0.0.1 (/playground)
error[E0382]: borrow of moved value: `x`
 --> src/main.rs:7:23
  |
2 |     let x: &mut i32 = &mut 0;
  |         - move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait
3 |     {
4 |         let y = x;
  |                 - value moved here
...
7 |     println!("x: {}", x);
  |                       ^ value borrowed here after move

error: aborting due to previous error

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

To learn more, run the command again with --verbose.

しかし、なぜかこっちは実行できる。

fn main() {
    let x: &mut i32 = &mut 0;
    {
        let y: &mut i32 = x;
        println!("y: {}", y);
    }
    println!("x: {}", x);
}

ブロックを無くして、 xy を共存させると、コンパイルできなくなる。

fn main() {
    let x: &mut i32 = &mut 0;
    let y: &mut i32 = x;
    println!("x + y: {}", *x + *y);
}
   Compiling playground v0.0.1 (/playground)
error[E0503]: cannot use `*x` because it was mutably borrowed
 --> src/main.rs:4:27
  |
3 |     let y: &mut i32 = x;
  |                       - borrow of `*x` occurs here
4 |     println!("x + y: {}", *x + *y);
  |                           ^^   -- borrow later used here
  |                           |
  |                           use of borrowed `*x`

error: aborting due to previous error

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

To learn more, run the command again with --verbose.

cannot use *x because it was mutably borrowed という不穏なメッセージが出ている。これは、 y に代入しているものが &mut *x であることを暗に示している。実際、最初の例を以下のようにすると実行可能になる。

fn main() {
    let x: &mut i32 = &mut 0;
    {
        let y = &mut *x;
        println!("y: {}", y);
    }
    println!("x: {}", x);
}

この振る舞いを調べると、どうやら reborrow と言われている振る舞いであることがわかった。しかし、現時点で reborrow について明示的にドキュメントされてないらしく、以下の issue が上がっている。

github.com

もっとも、この挙動はレシーバが &mut self であるようなメソッドから、同様にレシーバが &mut self であるメソッドを複数回呼ぶだけで自然に発生する(ただ、メソッドのレシーバは auto dereference が効くので、特殊な気もするが)もので、特筆しなくても常識だろうということなのかもしれない。とはいえ、 Copy じゃない割に move しているようにも見えなくて腹落ちしてなかったので、そこがはっきりしたのは調べた甲斐はあった。