You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-28 03:28:32 +02:00
Put Ref/Cell on its own slide (#1062)
This commit is contained in:
committed by
GitHub
parent
debaca9f7f
commit
8956fab9bb
@ -113,6 +113,7 @@
|
|||||||
- [Recursive Data Types](std/box-recursive.md)
|
- [Recursive Data Types](std/box-recursive.md)
|
||||||
- [Niche Optimization](std/box-niche.md)
|
- [Niche Optimization](std/box-niche.md)
|
||||||
- [Rc](std/rc.md)
|
- [Rc](std/rc.md)
|
||||||
|
- [Cell/RefCell](std/cell.md)
|
||||||
- [Modules](modules.md)
|
- [Modules](modules.md)
|
||||||
- [Visibility](modules/visibility.md)
|
- [Visibility](modules/visibility.md)
|
||||||
- [Paths](modules/paths.md)
|
- [Paths](modules/paths.md)
|
||||||
|
52
src/std/cell.md
Normal file
52
src/std/cell.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# `Cell` and `RefCell`
|
||||||
|
|
||||||
|
[`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.
|
||||||
|
|
||||||
|
`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.
|
||||||
|
|
||||||
|
```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);
|
||||||
|
|
||||||
|
println!("graph: {root:#?}");
|
||||||
|
println!("graph sum: {}", root.borrow().sum());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
* 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`.
|
||||||
|
* Demonstrate that reference loops can be created by adding `root` to `subtree.children` (don't try to print it!).
|
||||||
|
* 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>
|
@ -15,17 +15,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
* If you need to mutate the data inside an `Rc`, you will need to wrap the data in
|
* See [`Arc`][2] and [`Mutex`][3] if you are in a multi-threaded context.
|
||||||
a type such as [`Cell` or `RefCell`][2].
|
* You can *downgrade* a shared pointer into a [`Weak`][4] pointer to create cycles
|
||||||
* See [`Arc`][3] and [`Mutex`][4] if you are in a multi-threaded context.
|
|
||||||
* You can *downgrade* a shared pointer into a [`Weak`][5] pointer to create cycles
|
|
||||||
that will get dropped.
|
that will get dropped.
|
||||||
|
|
||||||
[1]: https://doc.rust-lang.org/std/rc/struct.Rc.html
|
[1]: https://doc.rust-lang.org/std/rc/struct.Rc.html
|
||||||
[2]: https://doc.rust-lang.org/std/cell/index.html
|
[2]: ../concurrency/shared_state/arc.md
|
||||||
[3]: ../concurrency/shared_state/arc.md
|
[3]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
||||||
[4]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
[4]: https://doc.rust-lang.org/std/rc/struct.Weak.html
|
||||||
[5]: https://doc.rust-lang.org/std/rc/struct.Weak.html
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
|
|
||||||
@ -34,37 +31,8 @@ fn main() {
|
|||||||
* `Rc::clone` is cheap: it creates a pointer to the same allocation and increases the reference count. Does not make a deep clone and can generally be ignored when looking for performance issues in code.
|
* `Rc::clone` is cheap: it creates a pointer to the same allocation and increases the reference count. Does not make a deep clone and can generally be ignored when looking for performance issues in code.
|
||||||
* `make_mut` actually clones the inner value if necessary ("clone-on-write") and returns a mutable reference.
|
* `make_mut` actually clones the inner value if necessary ("clone-on-write") and returns a mutable reference.
|
||||||
* Use `Rc::strong_count` to check the reference count.
|
* Use `Rc::strong_count` to check the reference count.
|
||||||
* Compare the different datatypes mentioned. `Box` enables (im)mutable borrows that are enforced at compile time. `RefCell` enables (im)mutable borrows that are enforced at run time and will panic if it fails at runtime.
|
|
||||||
* `Rc::downgrade` gives you a *weakly reference-counted* object to
|
* `Rc::downgrade` gives you a *weakly reference-counted* object to
|
||||||
create cycles that will be dropped properly (likely in combination with
|
create cycles that will be dropped properly (likely in combination with
|
||||||
`RefCell`).
|
`RefCell`, on the next slide).
|
||||||
|
|
||||||
```rust,editable
|
|
||||||
use std::rc::{Rc, Weak};
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Node {
|
|
||||||
value: i64,
|
|
||||||
parent: Option<Weak<RefCell<Node>>>,
|
|
||||||
children: Vec<Rc<RefCell<Node>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut root = Rc::new(RefCell::new(Node {
|
|
||||||
value: 42,
|
|
||||||
parent: None,
|
|
||||||
children: vec![],
|
|
||||||
}));
|
|
||||||
let child = Rc::new(RefCell::new(Node {
|
|
||||||
value: 43,
|
|
||||||
children: vec![],
|
|
||||||
parent: Some(Rc::downgrade(&root))
|
|
||||||
}));
|
|
||||||
root.borrow_mut().children.push(child);
|
|
||||||
|
|
||||||
println!("graph: {root:#?}");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
Reference in New Issue
Block a user