内部可変性
場合によっては、共有(読み取り専用)参照の背後にあるデータを変更する必要があります。たとえば、共有データ構造に内部キャッシュがあり、そのキャッシュを読み取り専用メソッドから更新する必要がある場合があります。
「内部可変性」パターンは、共有参照を通した排他的(可変)アクセスを可能にします。標準ライブラリには、これを安全に行うための方法がいくつか用意されており、通常はランタイム チェックを実行することで安全性を確保します。
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
RefCell
example is to end the borrow created by the call toborrow_mut
before we print the cell. Trying to print a borrowedRefCell
just shows the message"{borrowed}"
.
- The extra block in the
-
Cell
は安全性を確保するためのよりシンプルな手段であり、&self
を受け取るset
メソッドを備えています。ランタイム チェックは必要ありませんが、値を移動する必要があり、それによってコストが発生することがあります。 -
Both
RefCell
andCell
are!Sync
, which means&RefCell
and&Cell
can't be passed between threads. This prevents two threads trying to access the cell at once.