Cell, RefCell, UnsafeCellの違いとその使い分け

# 導入

[cell](https://doc.rust-lang.org/std/cell/index.html)モジュールには[Cell](https://doc.rust-lang.org/std/cell/struct.Cell.html), [RefCell](https://doc.rust-lang.org/std/cell/struct.RefCell.html), [UnsafeCell](https://doc.rust-lang.org/std/cell/struct.UnsafeCell.html)の三種類の構造体が定義されています。それらの違いを内部実装に軽く触れつつ解説したいと思います。
詳しい解説が不要な方は「[`cell`モジュール](#cellモジュール)」と「[使い分け](#使い分け)」をお読みください。

# 借用ルール

Rustの借用ルールについて復習しておきましょう。
Rustは参照に関して、可変な参照が唯一つ存在するか、不変な参照が一つ以上存在するかしか認めていません。もしこのルールがなければ以下のようなコードが通ることになります。

```rust
let mut v = vec![0, 1, 2];
let s = &v[1..]; // ここでvを借用した
v.pop(); // 既に借用されているvを可変に借用するので本当ならコンパイルエラー
println!("{}", s[1]); // 既にポップされた無効な値への参照を使用としている
```

このように借用ルールはきれいなプログラムを強制するためだけのものではなく、安全性のために必要不可欠ものであります。

# `cell`モジュール

上述のようにRustの借用ルールは必要なものですが、ある値を複数の場所から書き換えられたほうがいいこともあります。そこで借用ルールを迂回する際にどのような戦略を取るかに応じて、何でもできるがアンセーフな`UnsafeCell`, 値のみに制限した`Cell`, 参照も扱えるが少しオーバーヘッドのある`RefCell`が`cell`モジュールで定義されています。[^1]

[^1]: 借用ルールを迂回する型は他にも[Mutex](https://doc.rust-lang.org/std/sync/struct.Mutex.html)、[RwLock](https://doc.rust-lang.org/std/sync/struct.RwLock.html)、[atomic](https://doc.rust-lang.org/core/sync/atomic/index.html)があります。これらはcellと違ってスレッドを跨いで共有できる (`Sync`) のが特徴です。

## `UnsafeCell<T>`

`UnsafeCell<T>`は安全装置の付いていない[^2]cell型です。`UnsafeCell<T>`型は`T`型そのものとメモリ上でのレイアウトは同じで、`&UnsafeCell<T>`のような不変参照がある場合も内部の`T`型の値が書き換えられ得ることをコンパイラに伝えるだけの型です。

[^2]: `!Sync`であるという意味では安全装置がまったくないわけではありませんが

`UnsafeCell<T>`に対して定義されている重要な関数は次のものです。

```rust
pub fn get(&self) -> *mut T;
```

これは内部の値へのポインタを返す関数であり、このポインタを経由して内部の値を書き換えることができます。
ただし、このポインタを`&T`や`&mut T`に変換する際には通常通りRustの借用ルールに従っていなければなりません。すなわち可変参照`&mut T`が唯一つ存在するか、不変参照`&T`のみが一つ以上存在するという状況のいずれかでなければなりません。
このルールを破らないことが使用者に委ねられているという意味でアンセーフなCellです。

Rustから特別扱いされている型はこの`UnsafeCell`のみで、`Cell`と`RefCell`は`UnsafeCell`を用いることで標準ライブラリでなくとも実装することができます。

## `Cell`

これは`T`を値として扱うためのcell型です。内部的には`UnsafeCell<T>`をラップしているだけなのでオーバーヘッドがありません。

この型の特徴は`&Cell<T>`型から`&T`型の参照を得る関数が定義されていないことです。これにより内部の値への参照がないことが保証され、`pub fn set(&self, val: T)`のような値を書き換える関数の内部で一時的に`&mut T`型の参照を得ても他に`&T`型の参照が存在しないので借用ルールに反しないことが保証されます。
ここで気をつけなければならないことは、マルチスレッドで値を書き換える関数が同時に呼ばれて可変参照が唯一でなくなってしまう場合ですが、`Cell`は[!Sync](https://doc.rust-lang.org/std/cell/struct.Cell.html#impl-Sync), つまり複数のスレッドで共有できないようになっているので、同時に呼び出される可能性は除外されます。

内部の値への参照を得る方法がないということでしたが、では実際内部の値を使いたいときどうするかというと、主に以下の三種類の方法があります。

* `fn into_inner(self) -> T`: `Cell<T>`自体を消費して内部の値を取り出します
* `fn replace(&self, val: T) -> T`: 内部の値を他の値に置き換えて元の値を取り出します
* `fn get_mut(&mut self) -> &mut T`: 内部の値へのミュータブルな参照を得ます

`get_mut`は内部への参照を得る関数ですが、`Cell<T>`自体をミュータブルに借用して、新たに`Cell<T>`を借用する`set`等を呼び出せなくすることで整合性を保っています。

## `RefCell`

内部の`T`への参照を得ることもできる`Cell`の上位互換です。ただし借用ルールを実行時に検査するので少しのオーバーヘッドがあります。

`RefCell<T>`は`Cell<T>`の持つ関数に加えて、次の関数をサポートしています。

```rust
pub fn borrow(&self) -> Ref<'_, T>;
pub fn borrow_mut(&self) -> RefMut<'_, T>;
```

返り値が`&T`や`&mut T`ではなく`Ref<T>`や`RefMut<T>`になっているのが気になる方も多いはずなのでその理由を説明します。
`RefCell`は可変な借用が存在するときに`borrow`を呼び出したり、借用が存在するときに`borrow_mut`を呼び出したりするとパニックします。
これを実行時に確認するために`RefCell<T>`は借用の個数を管理する追加のフィールドを持っています。
借用が終わった際、自動的にカウントを減らす機能を`Ref<T>`や`RefMut<T>`の`Drop`として実装するため、`Ref`や`RefMut`を返すことになっています。

`Ref<T>`は`Deref`[^3] (`RefMut<T>`はそれに加えて`DerefMut`) を実装しているため必要に応じて自動的に`&T` (`RefMut<T>`は`&mut T`) に変換されるので、通常の参照と同様に使うことができます。

[^3]: Derefを知らない方向けの[記事](https://qiita.com/mosh/items/51bd202c9f738956829e)

# 使い分け

1. まず`Cell`を検討しましょう。書き換えたい値への参照が必要ない場合`Cell`で事足ります。
1. 参照が必要な場合は実行時に安全性を検査する`RefCell`を使うのが基本的な選択です。例えばプログラム全体で大きな構造体を静的変数に保持する場合などは、その値への参照がほしいことが多いはずなので`RefCell`が適しているでしょう。
1. `RefCell`の実行時のオーバーヘッドが許容できない場合は`UnsafeCell`を気をつけて使うことになります。

# あとがき

これまでなんとなくで`Cell`と`RefCell`を使っていたのですが、曖昧な理解だったので調べてみました。
同じように雰囲気で使っている人の理解に役立てば幸いです。