内部可変性
場合によっては、共有(読み取り専用)参照の背後にあるデータを変更する必要があります。たとえば、共有データ構造に内部キャッシュがあり、そのキャッシュを読み取り専用メソッドから更新する必要がある場合があります。
「内部可変性」パターンは、共有参照を通した排他的(可変)アクセスを可能にします。標準ライブラリには、これを安全に行うための方法がいくつか用意されており、通常はランタイム チェックを実行することで安全性を確保します。
Cell
Cell wraps a value and allows getting or setting the value using only a shared reference to the Cell. However, it does not allow any references to the inner value. Since there are no references, borrowing rules cannot be broken.
use std::cell::Cell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = Cell::new(5); cell.set(123); println!("{}", cell.get()); }
RefCell
RefCell allows accessing and mutating a wrapped value by providing alternative types Ref and RefMut that emulate &T/&mut T without actually being Rust references.
These types perform dynamic checks using a counter in the RefCell to prevent existence of a RefMut alongside another Ref/RefMut.
By implementing Deref (and DerefMut for RefMut), these types allow calling methods on the inner value without allowing references to escape.
use std::cell::RefCell; fn main() { // Note that `cell` is NOT declared as mutable. let cell = RefCell::new(5); { let mut cell_ref = cell.borrow_mut(); *cell_ref = 123; // This triggers an error at runtime. // let other = cell.borrow(); // println!("{}", *other); } println!("{cell:?}"); }
このスライドで重要なのは、Rust には、共有参照の背後にあるデータを変更する安全な方法が用意されているということです。安全性を確保するにはさまざまな方法がありますが、ここでは RefCell と Cell を取り上げます。
-
RefCellは、ランタイム チェックとともに Rust の通常の借用ルール(複数の共有参照または単一の排他参照)を適用します。この場合、すべての借用は非常に短く、重複しないため、チェックは常に成功します。- The extra block in the
RefCellexample is to end the borrow created by the call toborrow_mutbefore we print the cell. Trying to print a borrowedRefCelljust shows the message"{borrowed}".
- The extra block in the
-
Cellは安全性を確保するためのよりシンプルな手段であり、&selfを受け取るsetメソッドを備えています。ランタイム チェックは必要ありませんが、値を移動する必要があり、それによってコストが発生することがあります。 -
Both
RefCellandCellare!Sync, which means&RefCelland&Cellcan’t be passed between threads. This prevents two threads trying to access the cell at once.