Skip to content

Commit fdb45a9

Browse files
committed
finish off draft
1 parent 514e211 commit fdb45a9

File tree

1 file changed

+120
-18
lines changed

1 file changed

+120
-18
lines changed

src/ch15-00-smart-pointers.md

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ of `borrow` and `borrow_mut` methods, it has `get` and `set` methods:
479479
```rust
480480
use std::cell::Cell;
481481

482-
let mut five = Cell::new(5);
482+
let five = Cell::new(5);
483483

484484
five.set(6); // not five any more O_O
485485

@@ -499,14 +499,61 @@ But what is 'interior mutability' anyway?
499499

500500
## The Interior Mutability Pattern
501501

502-
The Interior Mutability Pattern is super unsafe internally but safe to use
503-
from the outside and is totally safe, totally, trust us, seriously, it's safe.
502+
*Interior mutability* is a design pattern in Rust for allowing you to mutate
503+
something that's immutable. Wait, what? Let's compare these two pieces of code.
504+
What's different about them?
504505

505-
Allude to `UnsafeCell<T>` maybe. Affects optimizations since &mut T is unique.
506-
UnsafeCell turns off those optimizations so that everything doesn't break.
506+
```rust
507+
use std::cell::Cell;
508+
509+
// one
510+
let mut five = 5;
511+
512+
five = 6;
513+
514+
// two
515+
let five = Cell::new(5);
516+
517+
five.set(6);
518+
```
519+
520+
There are three things that are different:
521+
522+
1. We use `5` in sample one, but `Cell::new(5)` in sample two.
523+
2. We use `=` in sample one, but `set` in sample two.
524+
3. `five` is mutable in sample one, but not in sample two.
525+
526+
That third difference? That's interior mutability.
527+
528+
Why are we allowed to do this? Well, to some degree, we're not. The reason we
529+
call interior mutability a "pattern" is that it's not really a language
530+
feature, it's a design pattern for libraries. More specifically, it's a pattern
531+
that uses `unsafe` code inside to bend Rust's usual rules. We haven't yet covered
532+
unsafe code in-depth, we will in Chapter 20. Luckily for us, you don't have to
533+
understand how it works inside to use it. All you need to know is that the
534+
various family of `Cell` types, as well as some others like `Mutex<T>` (that we'll
535+
cover in the next chapter, on concurrency) follow this pattern.
536+
537+
Why is this useful? Well, remember when we said that `Rc<T>` has to store
538+
immutable data? Given that `RefCell<T>` is immutable, but has interior mutability,
539+
we can combine `Rc<T>` and `RefCell<T>` to get a type that's both reference
540+
counted and mutable. Like this:
541+
542+
```rust
543+
use std::rc::Rc;
544+
use std::cell::RefCell;
545+
546+
let five = Rc::new(RefCell::new(5));
547+
548+
let other_rc = five.clone();
549+
550+
*other_rc.borrow_mut() = 6;
551+
```
507552

508-
This is how you can opt-out of the default of Rust's ownership rules and opt
509-
in to different guarantees.
553+
This is where interior mutability is useful: when you have something that
554+
requires immutability, but you also need to mutate something. This comes up
555+
with types like `Rc<T>`, but it can also come up in concurrency situations. In
556+
general, it's a fairly rare thing to need, but when you need it, it does exist.
510557

511558
### Cycles
512559

@@ -515,37 +562,92 @@ is deallocated. But what about this program?
515562

516563
```rust
517564
use std::rc::Rc;
565+
use std::cell::RefCell;
518566

519567
struct Cycle {
520-
really_bad: Option<Rc<Cycle>>,
568+
really_bad: RefCell<Option<Rc<Cycle>>>,
521569
leaked_data: i32,
522570
}
523571

524572
fn main() {
525573
let mut oh_no = Rc::new(Cycle {
526-
really_bad: None,
574+
really_bad: RefCell::new(None),
527575
leaked_data: 5,
528576
});
529577

530-
let clone = oh_no.clone();
578+
let clone = oh_no.clone();
531579

532-
oh_no.really_bad = Some(clone);
580+
*oh_no.really_bad.borrow_mut() = Some(clone);
533581
}
534582
```
535583

584+
There's a lot going on here. Fundamentally, `Cycle` is a type that contains an
585+
`Rc<Cycle>`. This means that we can have a 'referene cycle', hence the name of
586+
the struct. Here, we've constructed one in `main`: we have `oh_no`, and then we
587+
create a clone of it, `clone`. Since we've made a clone of it, the reference
588+
count is two: one for the initial `Rc<T>`, and one for the clone. When `oh_no`
589+
goes out of scope at the end of `main`, it will decrement the count to one. But
590+
that's it: `clone` never really goes out of scope, since it was moved into
591+
`oh_no`. This means that this memory is now unreachable, yet will never be
592+
cleaned up. It'll just sit there with a count of one, forever. In this
593+
specific case, the program ends right away, so it's not a problem, but in a
594+
more complex program, it would be.
595+
596+
Now, as you can see, doing this is very hard. To be honest, your authors had to
597+
look up previous discussions of an example to get this right. But it can
598+
happen, and the way that it happens is reasonably obvious: If you have an
599+
`Rc<T>` that contains an `Rc<T>`, beware.
600+
536601
#### Solution: turn an Rc into a `Weak<T>`
537602

538-
Same as Rc, but doesn't count towards the strong ref count. When you do this, the
539-
strong ref count goes down and the weak count goes up.
603+
To help with this problem, the Rust standard library contains a different
604+
type: `Weak<T>`. Let's see what that looks like:
605+
606+
```rust
607+
use std::rc::{Rc, Weak};
608+
use std::cell::RefCell;
609+
610+
struct Cycle {
611+
really_bad: RefCell<Option<Weak<Cycle>>>,
612+
leaked_data: i32,
613+
}
614+
615+
fn main() {
616+
let mut oh_no = Rc::new(Cycle {
617+
really_bad: RefCell::new(None),
618+
leaked_data: 5,
619+
});
620+
621+
let clone = Rc::downgrade(&oh_no.clone());
622+
623+
*oh_no.really_bad.borrow_mut() = Some(clone);
624+
}
625+
```
626+
627+
`Weak<T>` is exactly like `Rc<T>`, except that its reference count doesn't, well,
628+
count when determining if something should be freed. You can turn an `Rc<T>` into
629+
a `Weak<T>` with the `Rc::downgrade` associated method, which takes an `&Rc<T>`
630+
as an argument, and gives a `Weak<T>` back.
540631

541-
Data gets cleaned up when the strong count is 0, no matter what the weak count is.
632+
So in this example, when we call `oh_no.clone()`, we increment the count to two.
633+
But when we pass that clone to `downgrade`, that count goes down again, to one.
634+
Now, at the end of the function, when `oh_no` goes out of scope, it reduces the
635+
count from one to zero, and the memory is freed. Success! We've broken the cycle.
542636

543-
Why is the weak count needed then????
637+
If you're doing complex things with `Rc<T>`s, you should investigate if you
638+
have a cycle, and insert a `Weak<T>` so that your memory doesn't leak. The
639+
specifics depend on exactly what you're doing, but luckily, this isn't a
640+
Rust-specific idea; all of this translates over to other reference counting
641+
libraries in other languages, so doing some reading about it should help you.
544642

545643

546644
## Summary
547645

548-
If you want to implement your own smart pointer, go read the Nomicon.
646+
Whew! Smart pointers are powerful, but complex. We've covered the basics of
647+
smart pointers, and how to use some of the most common smart pointers.
648+
Implementing your own smart pointers is out of the scope of this book; you
649+
should check out the Nomicon if you're interested in building these kinds of
650+
abstractions.
549651

550-
Now let's talk about concurrency, and some smart pointers that can be used
551-
with multiple threads.
652+
Next, let's talk about concurrency in Rust. We'll even learn aobut a few new
653+
smart pointers that can help us with it.

0 commit comments

Comments
 (0)