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
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() {
let root = Node::new(1);
root.borrow_mut().children.push(Node::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);
// Note that `cell` is NOT declared as mutable.
let cell = RefCell::new(5);
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.
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>
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
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.
- The extra block in the `RefCell` example is to end the borrow created by the
call to `borrow_mut` before we print the cell. Trying to print a borrowed
`RefCell` just shows the message `"{borrowed}"`.
- `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`.
- 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>