1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2024-11-25 01:16:12 +02:00

Simplify interior mutability example (#2006)

The existing `RefCell` example code was more complex than necessary to
demonstrate the functionality, and was complex in a way that I often
found hard to explain to students. This PR replaces it with a much
simpler demonstration, and adds a code example for `Cell`.
This commit is contained in:
Nicole L 2024-04-19 07:21:30 -07:00 committed by GitHub
parent 0c23d817e5
commit ea8b5d2207
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -16,34 +16,21 @@ while still ensuring safety, typically by performing a runtime check.
```rust,editable ```rust,editable
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc;
#[derive(Debug, Default)]
struct Node {
value: i64,
children: Vec<Rc<RefCell<Node>>>,
}
impl Node {
fn new(value: i64) -> Rc<RefCell<Node>> {
Rc::new(RefCell::new(Node { value, ..Node::default() }))
}
fn sum(&self) -> i64 {
self.value + self.children.iter().map(|c| c.borrow().sum()).sum::<i64>()
}
}
fn main() { fn main() {
let root = Node::new(1); // Note that `cell` is NOT declared as mutable.
root.borrow_mut().children.push(Node::new(5)); let cell = RefCell::new(5);
let subtree = Node::new(10);
subtree.borrow_mut().children.push(Node::new(11));
subtree.borrow_mut().children.push(Node::new(12));
root.borrow_mut().children.push(subtree);
println!("graph: {root:#?}"); {
println!("graph sum: {}", root.borrow().sum()); let mut cell_ref = cell.borrow_mut();
*cell_ref = 123;
// This triggers an error at runtime.
// let other = cell.borrow();
// println!("{}", *other);
}
println!("{cell:?}");
} }
``` ```
@ -53,6 +40,18 @@ fn main() {
reference to the `Cell`. However, it does not allow any references to the value. reference to the `Cell`. However, it does not allow any references to the value.
Since there are no references, borrowing rules cannot be broken. Since there are no references, borrowing rules cannot be broken.
```rust,editable
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());
}
```
<details> <details>
The main thing to take away from this slide is that Rust provides _safe_ ways to The main thing to take away from this slide is that Rust provides _safe_ ways to
@ -64,20 +63,12 @@ that safety, and `RefCell` and `Cell` are two of them.
case, all borrows are very short and never overlap, so the checks always case, all borrows are very short and never overlap, so the checks always
succeed. succeed.
- `Rc` only allows shared (read-only) access to its contents, since its purpose - The extra block in the `RefCell` example is to end the borrow created by the
is to allow (and count) many references. But we want to modify the value, so call to `borrow_mut` before we print the cell. Trying to print a borrowed
we need interior mutability. `RefCell` just shows the message `"{borrowed}"`.
- `Cell` is a simpler means to ensure safety: it has a `set` method that takes - `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 `&self`. This needs no runtime check, but requires moving values, which can
have its own cost. have its own cost.
- Demonstrate that reference loops can be created by adding `root` to
`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
`thread 'main' panicked at 'already borrowed: BorrowMutError'`.
</details> </details>