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:
parent
0c23d817e5
commit
ea8b5d2207
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user