From 73068d2af2ac15d0c5c5efdc5c85ba330a683751 Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Wed, 17 Jan 2024 08:53:05 -0500 Subject: [PATCH] Improve interior mutability slide (#1683) Mutex is probably more broadly understood by people coming from other languages. Fixes #1512. --- src/borrowing/interior-mutability.md | 55 ++++++++++++++++----------- src/concurrency/shared_state/mutex.md | 3 +- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/borrowing/interior-mutability.md b/src/borrowing/interior-mutability.md index a229bfdb8d5f..8e09af63a7fe 100644 --- a/src/borrowing/interior-mutability.md +++ b/src/borrowing/interior-mutability.md @@ -2,26 +2,17 @@ minutes: 10 --- - - # 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; @@ -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. +
-- 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 diff --git a/src/concurrency/shared_state/mutex.md b/src/concurrency/shared_state/mutex.md index a5914bf9a35a..f9a06284e47d 100644 --- a/src/concurrency/shared_state/mutex.md +++ b/src/concurrency/shared_state/mutex.md @@ -1,7 +1,8 @@ # `Mutex` [`Mutex`][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;