1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-02-09 12:13:52 +02:00

Improve interior mutability slide (#1683)

Mutex is probably more broadly understood by people coming from other
languages.

Fixes #1512.
This commit is contained in:
Dustin J. Mitchell 2024-01-17 08:53:05 -05:00 committed by GitHub
parent d731da4a5e
commit 73068d2af2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 24 deletions

View File

@ -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.
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` and `RefCell`
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`](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.
`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;
@ -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

View File

@ -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;