-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Chapter 15: Smart pointers #407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
0d32bbe
4ad2690
ac61cbd
01e407a
092ff98
54e807c
12e12c8
b07ce30
3af06ba
5af5c49
8a39ec9
d2e6325
8d784f7
334c10e
a251d43
3595078
61d552e
77f697b
13b10e7
aa03fa1
6672fd6
bf02924
b460d00
5c6c1ab
e4658ca
c981fc0
84255f9
52d109f
90c380c
72b4ceb
d7ade00
6646733
90ba6d3
542013a
b13b292
0e27654
a17e7ca
07310e5
87609b1
5f6113d
3bfc407
364ff83
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,112 +1,36 @@ | ||
| # Smart Pointers | ||
|
|
||
| By smart pointers we mean a reference with more characteristics. | ||
|
|
||
| Example of something that doesn't work | ||
|
|
||
| Surprise! Vec and String are technically smart pointers too! | ||
|
|
||
| This chapter is not a comprehensive list, but will give some examples of the | ||
| ones in the standard library. | ||
|
|
||
|
|
||
| ## `Box<T>` | ||
|
|
||
| Don't use very often in your own code | ||
| Heap allocated | ||
| Express Ownership of a heap allocated thing | ||
|
|
||
| The three situations to use Box | ||
|
|
||
| 1. Trait objects | ||
| 2. Recursive data structures | ||
| 3. Extend the lifetime of something | ||
|
|
||
| How this interacts with the Drop trait | ||
|
|
||
| ## `Rc<T>` | ||
|
|
||
| Reference counted. Rc is for *multiple ownership* - this thing should get | ||
| deallocated when all of the owners go out of scope. | ||
|
|
||
| Show the data structure: | ||
|
|
||
| ```rust | ||
| struct Rc<T> { | ||
| data: Box<T>, | ||
| strong_reference_count: usize, | ||
| weak_reference_count: usize, | ||
| } | ||
| ``` | ||
|
|
||
| Talk through this. | ||
|
|
||
| This only works if the data is immutable. | ||
|
|
||
| What happens when you clone an Rc: data isn't cloned, increase the strong count. | ||
| When an Rc clone goes out of scope, the count goes down. | ||
|
|
||
| ### Rc Cycles | ||
|
|
||
| This is how you leak memory in rust, which btw is totally safe. | ||
|
|
||
| Is this garbage collecting? Well it's not tracing GC... if you use Rc and had | ||
| a cycle detector, it would be functionally equivalent to a tracing GC. Different | ||
| runtime characteristics tho. | ||
|
|
||
|
|
||
| #### Solution: turn an Rc into a `Weak<T>` | ||
|
|
||
| Same as Rc, but doesn't count towards the strong ref count. When you do this, the | ||
| strong ref count goes down and the weak count goes up. | ||
|
|
||
| Data gets cleaned up when the strong count is 0, no matter what the weak count is. | ||
| However, Rc structure is kept until weak reference count also goes to zero, so weak pointers do not become dangling pointers. | ||
| At this point, attempt to upgrade Weak pointer will result into None. | ||
| Only when weak reference counter also reduces to zero, Rc structure is freed. | ||
|
|
||
| ## `RefCell<T>` | ||
|
|
||
| Single owner of mutable data | ||
|
|
||
| The ownership rules checked at runtime instead of compile time. | ||
|
|
||
| Only single threaded. See next chapter. | ||
|
|
||
| ### `borrow` and `borrow_mut` methods | ||
|
|
||
| Checks all the rules and panics at runtime if the code violates them. | ||
|
|
||
| 1. The borrow checker is conservative and people can know more things. (no you | ||
| don't, but if you really want to go back to debugging segfaults, feel free) | ||
|
|
||
| 2. For when you're only allowed to have an immutable thing (which could be `Rc`) | ||
| but you need to be able to mutate the underlying data. | ||
|
|
||
| ## `Cell<T>` | ||
|
|
||
| Same thing as RefCell but for types that are Copy. No borrow checking rules here | ||
| anyway. So just reason #2 above. | ||
|
|
||
| ## Is this really safe? Yes! | ||
|
|
||
| RefCell is still doing the checks, just at runtime | ||
| Cell is safe bc Copy types don't need the ownership rules anyway | ||
|
|
||
| ### The Interior Mutability Pattern | ||
|
|
||
| The Interior Mutability Pattern is super unsafe internally but safe to use | ||
| from the outside and is totally safe, totally, trust us, seriously, it's safe. | ||
|
|
||
| Allude to `UnsafeCell<T>` maybe. Affects optimizations since &mut T is unique. | ||
| UnsafeCell turns off those optimizations so that everything doesn't break. | ||
|
|
||
| This is how you can opt-out of the default of Rust's ownership rules and opt | ||
| in to different guarantees. | ||
|
|
||
| ## Summary | ||
|
|
||
| If you want to implement your own smart pointer, go read the Nomicon. | ||
|
|
||
| Now let's talk about concurrency, and some smart pointers that can be used | ||
| with multiple threads. | ||
| Now that we've learned quite a bit of Rust, we can start digging into some more | ||
| complicated concepts. In this chapter, we'll learn about a design pattern in | ||
| Rust called a *smart pointer*. This pattern allows us to leverage Rust's | ||
| ownership and borrowing features to manage all kinds of resources in a safe | ||
| way, often without much more syntax than using plain old references. | ||
|
||
|
|
||
| So what are smart pointers, anyway? Well, we've learned about references in | ||
| Rust in Chapter 4. *Pointer* is a generic programming term for something like a | ||
| reference, that is, pointers "point at" data somewhere else. References are a | ||
| kind of pointer that only borrow data; by contrast, in many cases, smart | ||
| pointers *own* the data that they point to. They also often hold metadata about | ||
| the data. Smart pointers have extra capabilities that references don't, hence | ||
| the "smart" nickname. | ||
|
||
|
|
||
| We've actually already encountered a few smart pointers in this book, we didn't | ||
| call them that by name, though. For example, in a certain sense, `String` and | ||
| `Vec<T>` from Chapter 8 are both smart pointers. They own some memory and allow | ||
| you to manipulate it, and have metadata (like their capacity) and extra | ||
| capabilities or guarantees (`String` data will always be valid UTF-8). Another | ||
| good example is `File`, which we used for our I/O project in Chapter 12: it | ||
| owns and manages a file handle that the operating system gives us, and allows | ||
| us to access the data in the file. | ||
|
||
|
|
||
| Given that this is a general design pattern in Rust, this chapter won't cover | ||
| every smart pointer that exists. Many libraries will build their own as well, | ||
| and you may write some for your own code. The ones we cover here will be the | ||
|
||
| most common ones from the standard library: `Box<T>`, `Rc<T>`, and | ||
| `RefCell<T>`. Along the way, we'll also cover: | ||
|
|
||
| * The `Deref` and `Drop` traits that make smart pointers convenient to work with | ||
| * The *interior mutability* pattern that lets you... | ||
|
||
| * Reference cycles, how they can leak memory, and how to prevent them | ||
|
|
||
| Let's dive in! | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe these section titles can be fixed up a bit. The first 3 list the name of the thing right at the front, but then the
RcandRefCellones put their names at the end. If they were all at the front of the section names it would be more consistent and easier to find when flipping pages for a refresher.