このページの目次
Cell, RefCell, Mutex: 内部可変性
Rustの共有参照(
&T)はデフォルトで不変です——Cell/RefCell/Mutexは「共有」を前提として、異なるレベルの内部可変性を提供します。CellはCopy型に適しており(実行時オーバーヘッドなし)、RefCellは借用チェックを実行時に行い(パニックでエラー)、Mutexはスレッドセーフなロック(Send)を追加します。
問題の起源
let x = 5;
let r = &x; // &i32, 不変
// *r = 6; // ERROR: &T は変更できない
Rustの中核的なルール:共有参照(&T)を通じてデータを変更することはできません。これはデータ競合やイテレータの無効化を防ぎます。しかし、論理的には安全な変更がルールによって拒否されることがあります——例えば、計算結果のキャッシュや内部参照カウント。内部可変性は、&self を保持している間、内部状態を変更することを可能にします。
Cell: 値の置き換え(Copy型専用)
use Cell;
let c = new;
c.set; // 内部値を置き換え — &mut self は不要
let val = c.get; // コピーして取得
Cell は内部への参照を返しません——get() は常に値をコピーします(T: Copy が必要)、set() は常に値全体を置き換えます。外部に参照が漏れないため、borrow checker はこれを妨げません。適用シーン:単純な i32, bool, enum のラップ。
RefCell: 実行時 Borrow Check
use RefCell;
let rc = new;
// borrow_mut ガードのドロップ → 借用解放
let borrow = rc.borrow; // 実行時: OK
ルールはコンパイル時の borrow checker と同じ——エイリアシング XOR 変更——ですが、チェックは実行時に行われます。違反 → panic!(コンパイルエラーではありません)。borrow() は Ref<T>(&T への Deref)を返し、borrow_mut() は RefMut<T>(&mut T への Deref)を返します。これらのガードはスコープを抜ける際にドロップされ、内部の借用カウントを解放します。
RefCell は Sync ではありません——スレッド間で共有できません。
Mutex: 内部可変性 + Send + ロック
use Mutex;
let m = new;
let mut guard = m.lock.unwrap; // ロックを取得するまでブロック
*guard += 1; // DerefMut を通じて内部値にアクセス
drop; // ロック解放 (スコープ終了に依存も可)
Mutex は RefCell の内部可変性 + OSロック + Send 保証を組み合わせます。内部実装は UnsafeCell + OS mutex(Linux では pthread_mutex_t)です。lock() はロックを取得するまで現在のスレッドをブロックし、try_lock() はノンブロッキングで、すぐに Option<MutexGuard> を返します。
三者比較
| Cell | RefCell | Mutex | |
|---|---|---|---|
| 内部可変性 | Y | Y | Y |
| 参照の返却 | N (コピーのみ) | Y (Ref/RefMut) | Y (MutexGuard) |
| 競合処理 | N/A | panic | block |
| スレッドセーフ | N | N | Y |
| チェック時期 | コンパイル時 (借用なし) | 実行時 (panic) | 実行時 (block) |
参考
- Rust Book: Chapter 15.5
- Rustonomicon: UnsafeCell (すべての内部可変性の背後にあるプリミティブ)
Keywords: Cell, RefCell, Mutex, interior mutability, UnsafeCell, borrow_mut, runtime borrow check