Rustでmatch内部の所有権について考える（暗黙の借用 vs. ムーブ）

Rust の match が取る「式」と「ブランチ」の間で、所有権がどう扱われているのか考えたことはあるだろうか？ 私はなかった。普段、コンパイルエラーにならないので、なんとなく使っていた、というのが正直なところだ。

でも知らないと困ることもあるだろう。そこで、まずデフォルトが、「借用」と「ムーブ」のどちらになっているのかを確認し、さらに、デフォルトと違う動作をさせるにはどうやるのかを紹介しよう。

なお、この記事を書くにあたり、以下を参考にした。

- [Stuff the Identity Function Does (in Rust)](https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/)
- [Rust Users -- Reborrow vs. move](https://users.rust-lang.org/t/reborrow-vs-move/4796)

また、Rust の所有権システムについて、基本的なことを理解していると想定する。所有権システムについて学ぶには、日本語翻訳版のオフィシャルガイドを読むのがいいだろう。

- [プログラミング言語Rust -- 所有権](https://rust-lang-ja.github.io/the-rust-programming-language-ja/1.6/book/ownership.html)


## Rust のバージョン

今回紹介するコードは、Rust 1.9 ベータ版で動作確認済み。Rust 1.0 以降なら、どのバージョンでも問題なく動くはずだ。

```shell-session
/home/tatsuya% multirust run beta rustc --version --verbose
multirust: checking metadata version
multirust: got metadata version 2
rustc 1.9.0-beta.1 (37a2869af 2016-04-12)
binary: rustc
commit-hash: 37a2869afa7eb0813becaf0070874806030052df
commit-date: 2016-04-12
host: x86_64-unknown-freebsd
release: 1.9.0-beta.1

/home/tatsuya% freebsd-version 
10.3-RELEASE
```

安定版でなく、ベータ版を使用したのは、たいした理由ではない。FreeBSD 10.3-RELEASE 上で multirust を使用した時に、安定版のインストールができない不具合が起きているからだ。（FreeBSD では、[数日前から multirust が使えるようになったばかり](https://users.rust-lang.org/t/psa-rustc-cargo-can-now-be-installed-on-arm-linux-netbsd-and-freebsd-using-rustup-multirust/5383) で、まだ、こうした不具合があるようだ）


## 借用なのか、ムーブなのか

例として、単方向の linked list を使用する。

```rust
#[derive(Debug)]
struct List {
    value: i32,
    next: Option<Box<List>>,
}
```

`Box` は値を heap に置くので、それを要素として持つこの `List` では `Copy` トレイトは実装できない。従って、`List` を `let` で変数に束縛するときは、ムーブセマンティクスとなり、所有権が移動する。

では、`List` を使ってみよう。まず、２要素の適当なリストを返すメソッドを作っておく。

```rust
impl List {
    fn sample_list() -> Self {
        List{
            value: 1,
            next: Some(Box::new(List {
                value: 2,
                next: None,
            }))
        }
    }
}
```

**2016年4月17日：この章は書き直し中です**

match の head expression と branch の間の所有権の扱いについて、最初に記事を書いた時の理解が間違っていたようです。現在、正しい振る舞いを確認中です。

後半のミュータブルな暗黙の借用に対するテクニックは有効です。

`&mut` について、暗黙の再借用という振る舞いがあり、後半のテクニックはこれに関係しているようです。

```rust
// &mut がデフォルトで「ムーブ」のため、コンパイルエラーになるケース
fn ex1() {
    let mut i = 0;
    let mb1: &mut i32 = &mut i;
    {
        let mb2 = mb1;  // note: `mb1` moved here because it has type `&mut i32`,
                        // which is moved by default
        *mb2 = 1;
    }
    let mb3 = mb1;      // error: use of moved value: 'mb1' [E0382]
    *mb3 = 2;

    println!("i = {}", mb3);
}

// &mut の「暗黙の再借用」が起こり、コンパイルエラーにならないケース
fn ex2() {
    let mut i = 0;
    let mb1: &mut i32 = &mut i;
    {
        let mb2: &mut i32 = mb1;  // 暗黙の再借用
        *mb2 = 1;
    }
    let mb3 = mb1;
    *mb3 = 2;

    println!("i = {}", mb3);
}
```

## 暗黙の借用はイミュータブルなら問題ない

さて、２要素固定のプログラムだとつまらないので、どんな長さのリストでも扱えるようにしてみよう。まず、値の読み出しのみで、変更しないケース、つまり、イミュータブルな操作を実装してみよう。

`to_vec()` というメソッドを実装する。これは、`List` から、`Vec<i32>` 型に変換するメソッドだ。

```rust
impl List {
    fn to_vec(&self) -> Vec<i32> {
        let mut current = self;
        let mut result = Vec::new();
        loop {
            result.push(current.value);

            match current.next {
                None => break,
                Some(ref next_list) => current = next_list,
            }
        }
        result
    }
}
```

`current` にリストの先頭の要素を束縛し、`result.push(current.value)` で、値を `Vec<i32>` に追加している。match で、次の要素があるか調べ、あるなら、それを `current` に束縛して繰り返す。要素がなくなったら、`break` でループを抜け、`result` を返す。

使ってみよう。

```rust
fn main() {
    let mut list = List.sample_list()
    println!("{:?}", list.to_vec());
```

このケースでは、コンパイルエラーが起きず、以下のように、期待した結果が得られた。暗黙の借用でうまく動くわけだ。

```shell-session
[1, 2]
```


## ミュータブルだと問題あり

次は動かないケースを見よう。`add(n: i32)` という、リストの各要素に `n` の値を足すメソッドを作ってみる。

```rust
impl List {
    fn add(&mut self, n: i32) {
        let mut current = self;
        loop {
            current.value += n;

            match current.next {
                None => break,
                Some(ref mut next_list) => current = next_list,
            }
        }
    }
}

fn main() {
    let mut list = List::sample_list();
    println!("{:?}", list.to_vec());
    list.add(5);
    println!("{:?}", list.to_vec());
}
```

先ほどと構成は同じだ。変わったのは、引数が `&self` から `&mut self` ことくらいだ。

しかし、これはコンパイルエラーになる。

```shell-session
src/main.rs:50:22: 50:39 error: cannot borrow `current.next.0` as mutable more than once at a time [E0499]
src/main.rs:50                 Some(ref mut next_list) => current = next_list,
                                    ^~~~~~~~~~~~~~~~~
src/main.rs:50:22: 50:39 help: run `rustc --explain E0499` to see a detailed explanation
src/main.rs:50:22: 50:39 note: previous borrow of `current.next.0` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `current.next.0` until the borrow ends
src/main.rs:50                 Some(ref mut next_list) => current = next_list,
                                    ^~~~~~~~~~~~~~~~~
src/main.rs:53:6: 53:6 note: previous borrow ends here
src/main.rs:43     fn add(&mut self, n: i32) {
               ...
src/main.rs:53     }
                   ^
src/main.rs:50:44: 50:63 error: cannot assign to `current` because it is borrowed [E0506]
src/main.rs:50                 Some(ref mut next_list) => current = next_list,
                                                          ^~~~~~~~~~~~~~~~~~~
src/main.rs:50:22: 50:39 note: borrow of `current` occurs here
src/main.rs:50                 Some(ref mut next_list) => current = next_list,
                                    ^~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
error: Could not compile `reborrow_vs_move`.
```

元々 `current` には、`&mut self`、つまり、ミュータブルな参照が束縛されている。これが match で **再度** ミュータブルな借用をしてしまい、そこから取り出した値が `next_list` に束縛される。

ミュータブルな借用は、あるスコープ内では１つしか持てないので、`Some(ref mut next_list) => current = next_list` が成立せず、コンパイルエラーになるのだ。


## 強制的にムーブする

```rust
        loop {
            current.value += n;

            match current.next {
                None => break,
                Some(ref mut next_list) => current = next_list,
            }
        }
```

さて、ここをどうするかだが、まず安易な解決法として、match の所で `current` を `clone()`（複製）することが考えられる。

しかし、ループに注目すると、match　の前で `current` に元々束縛していたリストの要素は、match の後では、もう `current` に束縛されておらず、不要であるとわかる。すぐ不要になるのだから、`clone()` してしまうのは効率が悪い。

不要になるなら、match で所有権を移してかまわない。つまり、再借用ではなく、ムーブさせればいいわけだ。


### 関数でムーブする

match で `current` の値をムーブさせたいわけだが、Rust には、そのための専用構文はない。なぜなら、既存の仕組みでできるからだ。関数を使う方法と、ブロックを使う方法の、２つの方法がある。

まず、関数を使う方法を紹介しよう。以下のような関数を定義する。

```rust
fn moving<T>(x: T) -> T { x }
```

そして、match をこのように書き換える。

```rust
            match moving(current).next {
```

これでムーブが実現できた。

```shell-session
[1, 2]
[6, 7]
```


### 一体何が起きているのか？

でも、このシンプルな関数で、一体何が起こっているのか？ この関数は、任意の型の値を引数として受け取り、その値を返しているだけだ。これでどうしてムーブできるのか？

その理由は、引数の型にある。これは `x: T` であって、決して `x: &T` とか、 `x:&mut T` ではない。`x: T` ということは、この関数を呼び出した時に所有権が移動（ムーブ）する。そしてそのムーブ済みの値 `x` を返しているので、`match moving(current).next` で、ムーブ済みの戻り値にアクセスすることになり、狙い通りの動作となるわけだ。


### ブロックでムーブする

もうひとつの方法は、ブロック `{ }` を使うことだ。このように書き換える。

```rust
            match { current }.next {
```

これだけで、`moving()` 関数と同じくムーブが起こる。

実は、ブロックの中で変数にアクセスすると、それだけで、所有権がムーブするのだ。そして、`current` がそのブロックの最後の式の値、すなわち、ブロックからの戻り値になっているので、ムーブ済みの値の `.next` にアクセスできる。

この方法の欠点は、ここに `{ }` を付けてる意図が、数ヶ月たって読み返したときに、自分でも忘れているかもしれないことだろう。

またもうひとつの欠点として、`rustfmt` で自動整形した時に、以下のようになってしまい、読みにくいこともある。

```rust
            match {
                current
            }.next {
```


## まとめ

- match では、暗黙の借用が行われる。ほとんどの場面では、それで問題ない
- match の「式」のところに書いた変数に、「ブランチ」のところで再束縛し
  ようとすると問題が起こる
- その時は、`moving<T>(x: T) -> T { x }` というような関数か、ブロック
  `{ }` を使って、所有権をムーブすればいい