Skip to content

Commit 1a9f1eb

Browse files
committed
Ch. 17: Correct several points about Pin and Unpin
Thanks very much to @tmandry (<[email protected]>) for the corrections and feedback!
1 parent d9202d8 commit 1a9f1eb

File tree

2 files changed

+64
-31
lines changed

2 files changed

+64
-31
lines changed

nostarch/chapter17.md

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,10 +2536,19 @@ For more information about an error, try `rustc --explain E0277`.
25362536
When we read this error message carefully, it not only tells us that we need to
25372537
pin the values, but also tells us why pinning is required. The `trpl::join_all`
25382538
function returns a struct called `JoinAll`. That struct is generic over a type
2539-
`F`, which is constrained to implement the `Future` trait. Finally, directly
2540-
awaiting a Future requires that the future in question implement the `Unpin`
2541-
trait. That’s a lot! But we can understand it, if we dive a little further into
2542-
how the `Future` type actually works, in particular around *pinning*.
2539+
`F`, which is constrained to implement the `Future` trait. Directly awaiting a
2540+
future with `await` pins the future implicitly. That’s why we don’t need to use
2541+
`pin!` everywhere we want to await futures.
2542+
2543+
However, we’re not directly awaiting a future here. Instead, we construct a new
2544+
future, `JoinAll`, by passing a collection of futures to the `join_all`
2545+
function. The signature for `join_all` produces requires that the type of the
2546+
items in the collection all implement the `Future` trait, and `Box<T>` only
2547+
implements `Future` if the `T` that it wraps is a future which implements the
2548+
`Unpin` trait.
2549+
2550+
That’s a lot! But we can understand it, if we dive a little further into how the
2551+
`Future` type actually works, in particular around *pinning*.
25432552

25442553
Let’s look again at the definition of `Future`:
25452554

@@ -2683,24 +2692,32 @@ that a given type does *not* need to uphold any particular guarantees about
26832692
whether the value in question can be moved.
26842693

26852694
Just as with `Send` and `Sync`, the compiler implements `Unpin` automatically
2686-
for all types where it can prove it is safe. Implementing `Unpin` manually is
2687-
unsafe because it requires *you* to uphold all the guarantees which make `Pin`
2688-
and `Unpin` safe yourself for a type with internal references. In practice,
2689-
this is a very rare thing to implement yourself!
2695+
for all types where it can prove it is safe. The special case, again similar to
2696+
`Send` and `Sync`, is the case where `Unpin` is *not* implemented for a type.
2697+
The notation for this is `impl !Unpin for SomeType`, where `SomeType` is the
2698+
name of a type which *does* need to uphold those guarantees to be safe whenever
2699+
a pointer to that type it is used in a `Pin`.
2700+
2701+
In other words, there are two things to keep in mind about the relationship
2702+
between `Pin` and `Unpin`. First, `Unpin` is the “normal” case, and `!Unpin` is
2703+
the special case. Second, whether a type implements `Unpin` or `!Unpin` *only*
2704+
matters when using a pinned pointer to that type like `Pin<&mut SomeType>`.
26902705

26912706
To make that concrete, think about a `String`: it has a length and the Unicode
26922707
characters which make it up. We can wrap a `String` in `Pin`, as seen in Figure
2693-
17-7. However
2708+
17-8. However, `String` automatically implements `Unpin`, the same as most other
2709+
types in Rust.
26942710

26952711
<img alt="Concurrent work flow" src="img/trpl17-08.svg" />
26962712

26972713
Figure 17-8: Pinning a String, with a dotted line indicating that the String
26982714
implements the `Unpin` trait, so it is not pinned.
26992715

2700-
This means that we can do things such as replace one string with another at the
2701-
exact same location in memory as in Figure 17-9. This doesn’t violate the `Pin`
2702-
contract because `String`—like most other types in Rust—implements `Unpin`,
2703-
because it has no internal references that make it unsafe to move around!
2716+
As a result, we can do things which would be illegal if `String` implemented
2717+
`!Unpin` instead, such as replace one string with another at the exact same
2718+
location in memory as in Figure 17-9. This doesn’t violate the `Pin` contract,
2719+
because `String` has no internal references that make it unsafe to move around!
2720+
That is precisely why it implements `Unpin` rather than `!Unpin`.
27042721

27052722
<img alt="Concurrent work flow" src="img/trpl17-09.svg" />
27062723

@@ -2709,9 +2726,10 @@ Figure 17-9: Replacing the String with an entirely different String in memory.
27092726
Now we know enough to understand the errors reported for that `join_all` call
27102727
from back in Listing 17-17. We originally tried to move the futures produced by
27112728
async blocks into a `Vec<Box<dyn Future<Output = ()>>>`, but as we’ve seen,
2712-
those futures may have internal references, so they don’t implement `Unpin`.
2713-
They need to be pinned, and then we can pass the `Pin` type into the `Vec`,
2714-
confident that the underlying data in the futures will *not* be moved.
2729+
those futures may have internal references, so they don’t automatically
2730+
implement `Unpin`. Once we pin them, we can pass the resulting `Pin` type into
2731+
the `Vec`, confident that the underlying data in the futures will *not* be
2732+
moved.
27152733

27162734
`Pin` and `Unpin` are mostly important for building lower-level libraries, or
27172735
when you’re building a runtime itself, rather than for day to day Rust code.

src/ch17-05-traits-for-async.md

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ it is not yet ready.
115115

116116
### Pinning and the Pin and Unpin Traits
117117

118-
<!-- TODO: get a *very* careful technical review of this section! -->
119-
120118
When we introduced the idea of pinning while working on Listing 17-17, we ran
121119
into a very gnarly error message. Here is the relevant part of it again:
122120

@@ -152,10 +150,19 @@ For more information about an error, try `rustc --explain E0277`.
152150
When we read this error message carefully, it not only tells us that we need to
153151
pin the values, but also tells us why pinning is required. The `trpl::join_all`
154152
function returns a struct called `JoinAll`. That struct is generic over a type
155-
`F`, which is constrained to implement the `Future` trait. Finally, directly
156-
awaiting a Future requires that the future in question implement the `Unpin`
157-
trait. That’s a lot! But we can understand it, if we dive a little further into
158-
how the `Future` type actually works, in particular around *pinning*.
153+
`F`, which is constrained to implement the `Future` trait. Directly awaiting a
154+
future with `await` pins the future implicitly. That’s why we don’t need to use
155+
`pin!` everywhere we want to await futures.
156+
157+
However, we’re not directly awaiting a future here. Instead, we construct a new
158+
future, `JoinAll`, by passing a collection of futures to the `join_all`
159+
function. The signature for `join_all` produces requires that the type of the
160+
items in the collection all implement the `Future` trait, and `Box<T>` only
161+
implements `Future` if the `T` that it wraps is a future which implements the
162+
`Unpin` trait.
163+
164+
That’s a lot! But we can understand it, if we dive a little further into how the
165+
`Future` type actually works, in particular around *pinning*.
159166

160167
Let’s look again at the definition of `Future`:
161168

@@ -312,14 +319,21 @@ type does *not* need to uphold any particular guarantees about whether the value
312319
in question can be moved.
313320

314321
Just as with `Send` and `Sync`, the compiler implements `Unpin` automatically
315-
for all types where it can prove it is safe. Implementing `Unpin` manually is
316-
unsafe because it requires *you* to uphold all the guarantees which make `Pin`
317-
and `Unpin` safe yourself for a type with internal references. In practice,
318-
this is a very rare thing to implement yourself!
322+
for all types where it can prove it is safe. The special case, again similar to
323+
`Send` and `Sync`, is the case where `Unpin` is *not* implemented for a type.
324+
The notation for this is `impl !Unpin for SomeType`, where `SomeType` is the
325+
name of a type which *does* need to uphold those guarantees to be safe whenever
326+
a pointer to that type it is used in a `Pin`.
327+
328+
In other words, there are two things to keep in mind about the relationship
329+
between `Pin` and `Unpin`. First, `Unpin` is the “normal” case, and `!Unpin` is
330+
the special case. Second, whether a type implements `Unpin` or `!Unpin` *only*
331+
matters when using a pinned pointer to that type like `Pin<&mut SomeType>`.
319332

320333
To make that concrete, think about a `String`: it has a length and the Unicode
321334
characters which make it up. We can wrap a `String` in `Pin`, as seen in Figure
322-
17-7. However
335+
17-8. However, `String` automatically implements `Unpin`, the same as most other
336+
types in Rust.
323337

324338
<figure>
325339

@@ -329,10 +343,11 @@ characters which make it up. We can wrap a `String` in `Pin`, as seen in Figure
329343

330344
</figure>
331345

332-
This means that we can do things such as replace one string with another at the
333-
exact same location in memory as in Figure 17-9. This doesn’t violate the `Pin`
334-
contract because `String`—like most other types in Rust—implements `Unpin`,
335-
because it has no internal references that make it unsafe to move around!
346+
As a result, we can do things which would be illegal if `String` implemented
347+
`!Unpin` instead, such as replace one string with another at the exact same
348+
location in memory as in Figure 17-9. This doesn’t violate the `Pin` contract,
349+
because `String` has no internal references that make it unsafe to move around!
350+
That is precisely why it implements `Unpin` rather than `!Unpin`.
336351

337352
<figure>
338353

0 commit comments

Comments
 (0)