Skip to content

Commit

Permalink
Merge pull request rust-lang#407 from rust-lang/ch15-smart-pointers
Browse files Browse the repository at this point in the history
Chapter 15: Smart pointers
  • Loading branch information
carols10cents authored Mar 3, 2017
2 parents b2b4957 + 364ff83 commit b7f5dc5
Show file tree
Hide file tree
Showing 17 changed files with 1,888 additions and 110 deletions.
14 changes: 14 additions & 0 deletions dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ bool
boolean
booleans
Bors
BorrowMutError
BuildHasher
Cagain
callsite
Expand All @@ -52,16 +53,21 @@ CStr
CString
ctrl
Ctrl
CustomSmartPointer
deallocated
deallocating
deallocation
debuginfo
deps
deref
Deref
dereference
Dereference
dereferenced
dereferences
dereferencing
DerefMut
destructor
destructure
destructuring
Destructuring
Expand Down Expand Up @@ -158,6 +164,7 @@ monomorphization
Monomorphization
monomorphized
MoveMessage
multithreaded
Mutex
namespace
namespaced
Expand Down Expand Up @@ -197,11 +204,14 @@ RAII
randcrate
READMEs
rect
recurse
redeclaring
Refactoring
refactor
refactoring
refcell
RefCell
RefMut
repr
retweet
ripgrep
Expand Down Expand Up @@ -264,6 +274,8 @@ typeof
UFCS
unary
Unary
uncomment
Uncomment
Uninstalling
uninstall
unoptimized
Expand All @@ -276,11 +288,13 @@ usize
UsState
utils
variable's
variant's
vers
versa
Versioning
wasn
WeatherForecast
WebSocket
whitespace
wildcards
workspace
Expand Down
23 changes: 23 additions & 0 deletions dot/trpl15-01.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
digraph {
rankdir=LR;
dpi=300.0;
node [shape="plaintext"];

table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD><TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD>∞</TD></TR>
</TABLE></TD></TR>
</TABLE></TD></TR>
</TABLE></TD></TR>
</TABLE></TD></TR>
</TABLE>>];
}

17 changes: 17 additions & 0 deletions dot/trpl15-02.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
digraph {
rankdir=LR;
dpi=300.0;
node [shape="plaintext"];

table0[label=<
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD COLSPAN="2" SIDES="B">Cons</TD></TR>
<TR><TD>i32</TD><TD>
<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B">Box</TD></TR>
<TR><TD>usize</TD></TR>
</TABLE>
</TD></TR>
</TABLE>>];
}

50 changes: 50 additions & 0 deletions dot/trpl15-03.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
digraph {
rankdir=LR;
dpi=300.0;
node [shape="plaintext"];

table4[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B">b</TD><TD SIDES="B" PORT="ptr4"></TD></TR>
</TABLE>>];

table5[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte4">3</TD><TD PORT="ptr5"> </TD></TR>
</TABLE>>];


table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B">a</TD><TD SIDES="B" PORT="ptr0"></TD></TR>
</TABLE>>];

table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte0">5</TD><TD PORT="ptr1"> </TD></TR>
</TABLE>>];

table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte1">10</TD><TD PORT="ptr2"> </TD></TR>
</TABLE>>];

table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte2">Nil</TD></TR>
</TABLE>>];


table6[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B">c</TD><TD SIDES="B" PORT="ptr6"></TD></TR>
</TABLE>>];

table7[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte6">4</TD><TD PORT="ptr7"> </TD></TR>
</TABLE>>];


edge[tailclip="false"];
table0:ptr0:c -> table1:pte0;
table1:ptr1:c -> table2:pte1;
table2:ptr2:c -> table3:pte2;
table4:ptr4:c -> table5:pte4;
table5:ptr5:c -> table1:pte0;
table6:ptr6:c -> table7:pte6;
table7:ptr7:c -> table1:pte0;
}

32 changes: 32 additions & 0 deletions dot/trpl15-04.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
digraph {
rankdir="LR";
dpi=300.0;
node [shape="plaintext"];

table0[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B" PORT="ptr3">a</TD><TD SIDES="B" PORT="ptr0"></TD></TR>
</TABLE>>];

table1[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="pte0">5</TD><TD PORT="ptr1"> </TD></TR>
</TABLE>>];

table2[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD SIDES="B" PORT="pte1">b</TD><TD SIDES="B" PORT="pte2"></TD></TR>
</TABLE>>];

table3[label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
<TR><TD PORT="ptr2">10</TD><TD PORT="pte3"> </TD></TR>
</TABLE>>];

edge[tailclip="false"];
table0:ptr0:c -> table1:pte0;
table1:ptr1:c -> table2:pte1;
table3:ptr2 -> table2:pte2 [ dir="back" ];
table0:ptr3 -> table3:pte3:c;

{ rank="same"; table1; table2; }
{ rank="same"; table0; table3; }

}

6 changes: 6 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@
- [Extending Cargo with Custom Commands](ch14-05-extending-cargo.md)

- [Smart Pointers](ch15-00-smart-pointers.md)
- [`Box<T>` Points to Data on the Heap and Has a Known Size](ch15-01-box.md)
- [The `Deref` Trait Allows Access to the Data Through a Reference](ch15-02-deref.md)
- [The `Drop` Trait Runs Code on Cleanup](ch15-03-drop.md)
- [`Rc<T>`, the Reference Counted Smart Pointer](ch15-04-rc.md)
- [`RefCell<T>` and the Interior Mutability Pattern](ch15-05-interior-mutability.md)
- [Creating Reference Cycles and Leaking Memory is Safe](ch15-06-reference-cycles.md)

- [Concurrency](ch16-00-concurrency.md)

Expand Down
149 changes: 39 additions & 110 deletions src/ch15-00-smart-pointers.md
Original file line number Diff line number Diff line change
@@ -1,112 +1,41 @@
# 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.
*Pointer* is a generic programming term for something that refers to a location
that stores some other data. We learned about Rust's references in Chapter 4;
they're a plain sort of pointer indicated by the `&` symbol and borrow the
value that they point to. *Smart pointers* are data structures that act like a
pointer, but also have additional metadata and capabilities, such as reference
counting. The smart pointer pattern originated in C++. In Rust, an additional
difference between plain references and smart pointers is that references are a
kind of pointer that only borrow data; by contrast, in many cases, smart
pointers *own* the data that they point to.

We've actually already encountered a few smart pointers in this book, even
though we didn't call them that by name at the time. 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). The characteristics that distinguish a smart pointer from an
ordinary struct are that smart pointers implement the `Deref` and `Drop`
traits, and in this chapter we'll be discussing both of those traits and why
they're important to smart pointers.

Given that the smart pointer pattern is a general design pattern used
frequently in Rust, this chapter won't cover every smart pointer that exists.
Many libraries have their own and you may write some yourself. The ones we
cover here are the most common ones from the standard library:

* `Box<T>`, for allocating values on the heap
* `Rc<T>`, a reference counted type so data can have multiple owners
* `RefCell<T>`, which isn't a smart pointer itself, but manages access to the
smart pointers `Ref` and `RefMut` to enforce the borrowing rules at runtime
instead of compile time

Along the way, we'll also cover:

* The *interior mutability* pattern where an immutable type exposes an API for
mutating an interior value, and the borrowing rules apply at runtime instead
of compile time
* Reference cycles, how they can leak memory, and how to prevent them

Let's dive in!
Loading

0 comments on commit b7f5dc5

Please sign in to comment.