mirror of
https://github.com/google/comprehensive-rust.git
synced 2024-11-28 18:11:07 +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:
parent
0c23d817e5
commit
ea8b5d2207
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user