mutなスマポ (BoxとかRcとかCellとか)

スマートポインタのことをスマポって略してみました。


Rustのポインタ・所有権管理は本当にくっっそ面倒もとい厳格です。 使える武器を全部知っておかないと、ちょっとしたプログラムを書くのにも苦労間違いなしです。 だいたいのチュートリアルでは`Box`と`Rc`の簡単な使い方には触れますが、意外と`Cell`や`mut`変数の使い方に触れてるものが少ない(ような気がした)ので書きました。

(注：型を読者にわかりやすくするために、変数宣言では`let a : i32`のように型を記入していますが、本来ほとんどが省略可能です)

## `mut`な参照

```rust
let mut a : i32 = 1;
{
    let ref_to_a : &mut i32 = &mut a;
    *ref_to_a = 32;
}
println!("a = {}", a);  // a = 32
```

これはまぁ、普通に言語チュートリアルでも出てくるようなプログラムです。
このプログラム中に`mut`キーワードが3回出てきますが、このうちどれを消してもコンパイルが通らなくなります。 それぞれの`mut`の意味については、まぁなんとなくわかりますよね？ でもここではいちいち書き下します。

1. 最初の`mut`は、変数`a`の値が変更可能であることを示します。
2. 2番目の`mut`は、変数`ref_to_a`は「参照先**の値**を変更可能な」参照型であることを示します。
3. 3番目の`mut`は、変数`a`から「参照先**の値**を変更可能な」参照をとってくることを示します。

実はこのプログラム中には、もう1ヶ所`mut`を挿入できるところがあります。 ここです。

```rust
    //  vvv ここ！！
    let mut ref_to_a : &mut i32 = &mut a;
```

ここに`mut`を挿入すると、変数`ref_to_a`は「**参照先を**変更可能な」変数であることを示します。 2.と3.の、「参照先**の値**を変更可能な」とは意味が違います。 この`mut`があると、例えばこんなことができます。

```rust
let mut a : i32 = 1;
let mut b : i32 = 2;
{
    let mut ref_to_a : &mut i32 = &mut a;
    ref_to_a = &mut b;  // ここでaの代わりにbを参照させる
    *ref_to_a = 32;
}
println!("a = {}, b = {}", a, b); // a = 1, b = 32
```

どうでしょう。 C/C++のconst ptrの概念に慣れた人ならぱっとお分かりいただけるかと思いますが、constのない言語しかやったことのない人はちょっと混乱するかもしれません。 しかし、この区別はRustをやる上で避けて通れない区別です。 がんばってなんとなく理解してください。

## `mut`な`Box`、`mut`な`Rc`？
これの発展です。 参照のかわりに、`Box`や`Rc`を使うケースではどうでしょう(`Box`や`Rc`の意味についてはここでは書きません、その辺のドキュメントを読んでください)。 ここでは`Rc`を例に挙げます。
やはりここでも、「参照先**の値**を変更可能な」ものと、「**参照先を**変更可能な」ものを区別して考える必要があります。

「**参照先を**変更可能な」`Rc`の方は比較的わかりやすいです。 `mut`を変数名の前に追加してやるだけです。

```rust:参照先を変更可能なRc
use std::rc::Rc;
let mut a : Rc<i32> = Rc::new(1);
a = Rc::new(32);  // ここで参照先を変更している
println!("a = {}", *a); // a = 32
```

しかし、「参照先**の値**を変更可能な」`Rc`というのは一筋縄ではいきません。 参照を使ったコードから類推して、`Rc<>`の中に`mut`を書きたくなりますが、これはコンパイルできません。

```rust:コンパイルできない
use std::rc::Rc;
let a : Rc<mut i32> = Rc::new(1);
*a = 32;
println!("a = {}", *a); // a = 32
```

```text:エラーメッセージ
error: expected identifier, found keyword `mut`
let a : Rc<mut i32> = Rc::new(1);
```

特にC/C++をやったことのある人が起こしやすい勘違いなのですが、Rustの`mut`は、C++の`const`と違い、型の修飾子ではありません。 `mut`が書けるのは、基本的に変数宣言の`let`の直後か、参照の`&`の直後だけと覚えておきましょう(一応他にもありますが)。

ではどうすれば、Rustで合法的に`Rc`の参照先の値を書き換えられるのでしょうか。

## `Rc::make_mut`と`Rc::get_mut`

まずは不正解の答えから行きます。 `Rc`型のドキュメントを読んでいると、どうやらこの2つの関数を使えば、中身の`&mut T`型にアクセスできそうな気がしてきます。 しかし実は、この2つの関数には非常に強い制約があり、あなたの期待しているものではない可能性が高いです。

どういうことかというと、どちらの関数も「その`Rc`の参照カウントが1のときだけ」しか、参照先の`&mut`を返してくれないのです。

```rust
use std::rc::Rc;
let mut a : Rc<i32> = Rc::new(1);  // Rcを新しく作ったので、参照カウントは1
*Rc::make_mut(&mut a) += 1;  // make_mutを使って、aの参照先に1を足した
println!("a = {}", *a);  // a = 2

// Rcをコピーしたので、参照カウントは2
let b : Rc<i32> = a.clone();
println!("a = {}, b = {}", *a, *b);  // a = 2, b = 2

// aの参照先の値に1を足したい……もちろんbの参照先の値も1増えるはず？？？
*Rc::make_mut(&mut a) += 1;
println!("a = {}, b = {}", *a, *b);  // a = 3, b = 2  bは増えてない！
```

2回目に`make_mut`を呼び出したときには、1回目のときとは違い`a`の参照カウントは2になっています。 すると、`make_mut`は`i32::clone()`を呼び、新しい`Rc`とその参照先を作成してしまうのです。 なんでこんな不便な仕様なの？ というのはめんどくさいので触れません。 代わりにどうすればいいのかを考えましょう。

## `Cell`を使う

そこで登場するのが`Cell`です。  `Cell`は`Box`と同じように、他の型1個をラップするオブジェクトで、`get`と`set`メソッドを提供します。

```rust
let a : Cell<i32> = Cell::new(1);
a.set(32);
println!("a = {}", a.get());  // a = 32
```

これを見るとなんでわざわざ`Cell`なんて必要なんだ、と思われるかもしれませんが、よくよく見てください。 `a`は`mut`な変数ではないのに、`set`メソッドが呼び出せていますね。 `Cell`型は、`mut`でなくても`set`メソッドを呼び出すことができます。 つまり、さっき書こうとしていた、`Rc`の参照先**の値**に1を足してみるコードというのは、このように書くことができるのです。

```rust
let a : Rc<Cell<i32>> = Rc::new(Cell::new(1));
(*a).set((*a).get() + 1);
println!("a = {}", (*a).get());  // a = 2
```

`Rc`と`Cell`の二重構造になってややこしいと思いますか？ **これがRustだ。** 一応ちょっとだけ簡略化できて、`Rc`型が`Deref` traitを実装しているおかげで、`(*a).get()`のような部分は`a.get()`のように書けます。

これなら、`Rc`の中に`mut`と書くことなく、参照先**の値**を変更可能な`Rc`を作ることができます。

さて、これだけ見ると、`Cell`はただ単に普通の変数の`mut`制限をゆるくしただけの「軟派な」型のように見えますが、そうではありません。 普通の変数にはできるけど`Cell`にはできないことというのが結構あります。

1. `Cell`の中身の型は`Copy`をimplしていなければならない (i.e. memcpyでコピーできなければならない)
2. `Cell`は参照やスマートポインタなどを含め、スレッド間でやりとりすることができない
3. `Cell`は中身の参照を取ってくることができない

特に2.の制約がわかりやすく強力で、`Cell`型は1つのスレッドからしか読み書きされないことが保証されているので、いくつ変数のaliasが作られて何ヶ所から書き込まれようとも、データが同時書き込みや書き込み中の読み込みによって壊れてしまう心配がありません。 なので、`mut`とマークされていなくても`set`を呼び出せるようになっているのです。

## `RefCell`を使う

しかし、`Cell`の制限はちょっときつすぎます。 一番きついのは1.で、`Copy`な型というのは単純な整数型や、整数型のみからなる`struct`などに限られ、`HashMap`などのように内部にポインタ的なものを含むような型には使えません。

また3.の条件も地味にきついです。 例えば超長い配列を`Cell`に入れたとすると、`get`や`set`するたびにいちいちコピーが発生します。 超長い配列のうち1番目の要素だけを変更したい場合でも、いちいち全体をコピーしてきて1番目を書き換え、また全体を書き戻す……という作業が必要になります (たぶんここに最適化は効きません)。

なので、たいていの場合はそこの制限が緩和された`RefCell`を使います。 `RefCell`はその名の通り`Cell`の中に参照型を持ってるようなもので、`Cell`と比べて以下のような利点があります：

1. `RefCell`の中身の型はだいたい何でもいい (`Sized`)
2. `RefCell`は中身を参照や`mut`参照で取ってくることができる (`Ref`や`RefMut`経由)
3. `RefCell`の中身を参照で取れるということは、その参照を他のスレッドに安全に渡すこともできる

なんだ`Cell`の欠点が全部取り除かれてて最強じゃないか、と思いますが、ちょっと違うのは`Cell`の`get`と`set`メソッドが取り除かれていて、代わりに参照っぽい型の`Ref`型を返す`borrow`と、`mut`参照っぽい型の`RefMut`を返す`borrow_mut`メソッドで置き換えられていることです。 この2つのメソッドと戻り値には、普通の変数の`&`と`&mut`に似た特別なルールがあり、

1. `Ref`型のオブジェクトは同時に何個でも存在できる
2. `RefMut`型のオブジェクトが1つでも存在すると、他の`Ref`型および`RefMut`型のオブジェクトは存在できない

というルールがあります。

この`RefCell`が普通の変数と決定的に違う点は、このルール違反が起こった時に、普通の変数ならコンパイルエラーが出ますが、`RefCell`では**実行時に**`panic!`して落ちるという点です。 `RefCell`を使う際には、プログラマの責任で`borrow_mut`の呼び出し場所をきっちり管理してやる必要が生じます。 これを多用しすぎると、くっそ面倒くさいRustでコードを書く意義が薄れてしまうので、必要のないところではできるだけ使わないようにしましょう。

ただし、これが必ず必要になる場面というのがいくつか存在します。 詳しくは`std::cell`の公式ドキュメントを読んでください。

## まとめ
- `Rc`の参照先の値を変更したいときは、だいたい`Rc<RefCell<T>>`を使う。 かなりの頻出パターンなので覚えておきたい
- 1%ぐらいの確率で`Rc<Cell<T>>`や`Rc::get_mut, Rc::make_mut`が使えるかもしれないので、一応ドキュメントを読んでおく
- でも本当はあんまりそういうコードを書きたくない