Interior Mutability
In some situations, it’s necessary to modify data behind a shared (read-only) reference. For example, a shared data structure might have an internal cache, and wish to update that cache from read-only methods.
The “interior mutability” pattern allows exclusive (mutable) access behind a shared reference. The standard library provides several ways to do this, all while still ensuring safety, typically by performing a runtime check.
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.
1. ```
use std::cell::Cell;
```
3. ```
fn main() {
```
4. ```
// Note that `cell` is NOT declared as mutable.
```
5. ```
let cell = Cell::new(5);
```
7. ```
cell.set(123);
```
8. ```
println!("{}", cell.get());
```
9. ```
}
```
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.
1. ```
use std::cell::RefCell;
```
3. ```
fn main() {
```
4. ```
// Note that `cell` is NOT declared as mutable.
```
5. ```
let cell = RefCell::new(5);
```
7. ```
{
```
8. ```
let mut cell_ref = cell.borrow_mut();
```
9. ```
*cell_ref = 123;
```
11. ```
// This triggers an error at runtime.
```
12. ```
// let other = cell.borrow();
```
13. ```
// println!("{}", *other);
```
14. ```
}
```
16. ```
println!("{cell:?}");
```
17. ```
}
```
This slide should take about 10 minutes.
The main thing to take away from this slide is that Rust provides safe ways to modify data behind a shared reference. There are a variety of ways to ensure that safety, and RefCell and Cell are two of them.
-
RefCellenforces Rust’s usual borrowing rules (either multiple shared references or a single exclusive reference) with a runtime check. In this case, all borrows are very short and never overlap, so the checks always succeed. -
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}". Cellis a simpler means to ensure safety: it has asetmethod that takes&self. This needs no runtime check, but requires moving values, which can have its own cost.- Both
RefCellandCellare!Sync, which means&RefCelland&Cellcan’t be passed between threads. This prevents two threads trying to access the cell at once.
