&mutが一つしか作れないことに納得できないRustaceanへ

# ボローチェッカーとの闘い

Rust はボローチェッカーと戦う言語だといわれることがありますが、その戦いのゴングは次のようなコードで初めて鳴るのではないでしょうか。


```rust
    let mut x = 1;
    
    let r = &x;
    
    x = 2;
    
    println!("{}", r);
```

このようなコードは次のようなエラーで怒られます[^1]。

```
error[E0506]: cannot assign to `x` because it is borrowed
 --> src/main.rs:6:5
  |
4 |     let r = &x;
  |             -- borrow of `x` occurs here
5 |     
6 |     x = 2;
  |     ^^^^^ assignment to borrowed `x` occurs here
7 |     
8 |     println!("{}", r);
  |                    - borrow later used here

error: aborting due to previous error; 1 warning emitted
```

他の言語にはこのようなルールは無いので、初めて出くわしたときは面食らう人が多いと思います。

[The book](https://doc.rust-jp.rs/book-ja/ch04-02-references-and-borrowing.html) には、このルールは次のように書かれています。

* 任意のタイミングで、一つの可変参照か不変な参照いくつでものどちらかを行える。

この文章は微妙にわかりにくいと思いますが、要は下記のテーブルのように、 & は複数持てる、 &mut は一つだけ持てる、ただし & と &mut は同時には持てない、ということです。

|   | & | &mut |
|:-:|:-:|:-:|
| OK | 2  | 0  |
| OK  |  0 | 1  |
| ダメ  |  1 | 1  |
| ダメ  |  0 | 2  |

また、このルールは「一つのスコープの中で」適用されるので、関数呼び出しなどを通して新たなスコープを導入すれば、可変な参照が同時に2つ以上存在することは可能です。

```rust
fn f(x: &mut i32) -> i32 {
    println!("{}", x);
    *x
}
fn main() {
    let mut x = 1;
    let y = &mut x;
    f(y);
    println!("{}", x);
}
```

まぁ、そういうルールがあるってのはわかるけど、それがデータ安全性にどう役立つの？というところは The Book にも突っ込んで書かれていないので、もやもやする人も多いと思います。 RAII のように C++ でも馴染みのある機構でもないのでなおさらです。


# Aliasing

可変な借用が複数あるとまずいのは、aliasingの問題のためです。aliasingとは同じ変数を複数の名前でアクセスすることです。たとえば

```Rust
fn f(a: &mut i32, b: &mut i32) {
    *a = 1;
    *b = 2;
    *b = *a;
}
```

という関数は、 `*a` と `*b` に最終的に 1 が代入されるので、`*b = 2` は最適化で除去することができるように見えます。しかし呼び出し元が下記のように同じ変数への参照を `a` と `b` に渡していた場合、実際には `a` に 2 が代入されるロジックになるので、最適化によってロジックが変わってしまいます。

```Rust
    let mut a = 0;
    f(&mut a, &mut a);
```

このため、CおよびC++ではポインタや参照が同じ型を持っていた場合、同じ実体を参照している可能性があるとみなして最適化を制限します。これをstrict aliasingといい、FortranがCより速いといわれることがある理由です。Rustではこのように同じ実体への複数の参照を作ること自体ができませんので、オプティマイザはaliasingの可能性を考慮せずに最適化できます。

また、aliasingしている型がenum型だった場合はさらに深刻なことが起きます。

```Rust
fn g(a: &Option<i32>, b: &mut Option<i32>) {
    if let Some(ref v) = a {
        *b = None;
        println!("{}", v);
    }
}
```

この関数ではRustの標準データ型である `Option` を使っていますが、 `*a` が `Some` だったときに `*b` に `None` を代入しています。しかしもし呼び出し元が同じ実体への参照を渡すと、

```Rust
    let mut c = Some(1);
    g(&c, &mut c);
```

`v` という変数は `Some` の中身を参照しているので、他の参照を介して `None` に変更されてしまうと、無効なバリアントを参照することになります。これは恐るべき未定義動作で、プログラムがどんな動作をしてもおかしくありません。この例では不変な参照1個と、可変な参照1個で未定義動作が生じてしまいます。このためRustでは可変な参照があるときは不変な参照は1つも作れません。

# まとめ

このようにRustの借用のルールは、データの整合性を静的に保証するために必要なものです。もしこのルールがなければ、穴のある保証になってしまいます。理由が分かれば、ボローチェッカーは戦う相手ではなく、安心して背中を任せられる戦友となるのではないでしょうか。

なお、ボローチェッカーは静的なチェック機構ですが、たまに実行時に参照のルールをチェックする必要が生じます。木構造やリファレンスカウンタ式のスマートポインタなどです。この場合には [RefCell](https://doc.rust-jp.rs/book-ja/ch15-05-interior-mutability.html) が使われます。よく見られるのは `Rc<RefCell<Type>>` といったような型です。


## よくある誤解

データの整合性の保証には、必ずしもマルチスレッドが関わっているわけではありません。ボローチェッカーのルールはあくまでもシングルスレッドの aliasing の問題のために作られたものです。マルチスレッドプログラミングでの競合を避けるのにも役立つというのは、後になって発見されたことです。

また、ボローチェッカーだけをオフにできないかというのもたまに聞かれますが、ボローチェッカーはデータの整合性を保証するために必要なものであり、単独でOn/Offできるようなものではありません。ボローチェッカーがなければ strict aliasing の対策などを導入する必要が出てきて、言語設計が大幅に影響を受けてしまいます[^2]。これは Rust 言語の根幹に結びついており、言語の一部となっているといっていいでしょう。

私も最初にこのルールを見たときは納得がいきませんでした。"The book"は良く書かれたドキュメントですが、この点についてもう少し掘り下げて説明してほしかったと思います。

[^1]: 最後の `println!` を削除すれば、 2018 Edition ではコンパイルできます。 Non-lexical Lifetime と呼ばれる機能によるものです。
[^2]: unsafe ブロックを使えばボローチェッカーもろとも静的チェックをすべて無効にできますが、これは生の C++ よりも危険なコードです。