Skip to content

Commit

Permalink
Improve interior mutability slide (#1683)
Browse files Browse the repository at this point in the history
Mutex is probably more broadly understood by people coming from other
languages.

Fixes #1512.
  • Loading branch information
djmitche authored Jan 17, 2024
1 parent d731da4 commit 73068d2
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 24 deletions.
55 changes: 32 additions & 23 deletions src/borrowing/interior-mutability.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,17 @@
minutes: 10
---

<!-- NOTES:
Introduce the concept, with an example based on Mutex showing an `&self` method doing mutation; reference Cell/RefCell without detail.
-->

# Interior Mutability

Rust provides a few safe means of modifying a value given only a shared
reference to that value. All of these replace compile-time checks with runtime
checks.

## `Cell` and `RefCell`
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.

[`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html) and
[`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) implement
what Rust calls _interior mutability:_ mutation of values in an immutable
context.
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` is typically used for simple types, as it requires copying or moving
values. More complex interior types typically use `RefCell`, which tracks shared
and exclusive references at runtime and panics if they are misused.
## `RefCell`

```rust,editable
use std::cell::RefCell;
Expand Down Expand Up @@ -56,16 +47,34 @@ fn main() {
}
```

## `Cell`

`Cell` wraps a value and allows getting or setting the value, even with a shared
reference to the `Cell`. However, it does not allow any references to the value.
Since there are no references, borrowing rules cannot be broken.

<details>

- If we were using `Cell` instead of `RefCell` in this example, we would have to
move the `Node` out of the `Rc` to push children, then move it back in. This
is safe because there's always one, un-referenced value in the cell, but it's
not ergonomic.
- To do anything with a Node, you must call a `RefCell` method, usually `borrow`
or `borrow_mut`.
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.

- `RefCell` enforces 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.

- `Rc` only allows shared (read-only) access to its contents, since its purpose
is to allow (and count) many references. But we want to modify the value, so
we need interior mutability.

- `Cell` is a simpler means to ensure safety: it has a `set` method that takes
`&self`. This needs no runtime check, but requires moving values, which can
have its own cost.

- Demonstrate that reference loops can be created by adding `root` to
`subtree.children` (don't try to print it!).
`subtree.children`.

- To demonstrate a runtime panic, add a `fn inc(&mut self)` that increments
`self.value` and calls the same method on its children. This will panic in the
presence of the reference loop, with
Expand Down
3 changes: 2 additions & 1 deletion src/concurrency/shared_state/mutex.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# `Mutex`

[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`
behind a read-only interface:
behind a read-only interface (another form of
[interior mutability](../../borrowing/interior-mutability)):

```rust,editable
use std::sync::Mutex;
Expand Down

0 comments on commit 73068d2

Please sign in to comment.