Skip to content

Commit a4e350b

Browse files
committed
ch06: fix Join combinator with explicit Unpin impl + Box::pin
MaybeDone<F> stores F::Output, so auto-Unpin fails even when F: Unpin. Add manual impl Unpin for Join<A,B> since we never pin-project into fields. Update poll to use self.get_mut(). Update usage example to show Box::pin for async blocks.
1 parent 7a73b6c commit a4e350b

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

async-book/src/ch06-building-futures-by-hand.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,12 @@ enum MaybeDone<F: Future> {
105105
Taken, // Output has been taken
106106
}
107107

108+
// MaybeDone<F> stores F::Output, which the compiler can't prove
109+
// is Unpin even when F: Unpin. Since we only use Join with Unpin
110+
// futures and never pin-project into fields, implementing Unpin
111+
// by hand is safe and lets us call self.get_mut() in poll().
112+
impl<A: Future + Unpin, B: Future + Unpin> Unpin for Join<A, B> {}
113+
108114
impl<A, B> Join<A, B>
109115
where
110116
A: Future,
@@ -125,30 +131,32 @@ where
125131
{
126132
type Output = (A::Output, B::Output);
127133

128-
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
134+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
135+
let this = self.get_mut();
136+
129137
// Poll A if not done
130-
if let MaybeDone::Pending(ref mut fut) = self.a {
138+
if let MaybeDone::Pending(ref mut fut) = this.a {
131139
if let Poll::Ready(val) = Pin::new(fut).poll(cx) {
132-
self.a = MaybeDone::Done(val);
140+
this.a = MaybeDone::Done(val);
133141
}
134142
}
135143

136144
// Poll B if not done
137-
if let MaybeDone::Pending(ref mut fut) = self.b {
145+
if let MaybeDone::Pending(ref mut fut) = this.b {
138146
if let Poll::Ready(val) = Pin::new(fut).poll(cx) {
139-
self.b = MaybeDone::Done(val);
147+
this.b = MaybeDone::Done(val);
140148
}
141149
}
142150

143151
// Both done?
144-
match (&self.a, &self.b) {
152+
match (&this.a, &this.b) {
145153
(MaybeDone::Done(_), MaybeDone::Done(_)) => {
146154
// Take both outputs
147-
let a_val = match std::mem::replace(&mut self.a, MaybeDone::Taken) {
155+
let a_val = match std::mem::replace(&mut this.a, MaybeDone::Taken) {
148156
MaybeDone::Done(v) => v,
149157
_ => unreachable!(),
150158
};
151-
let b_val = match std::mem::replace(&mut self.b, MaybeDone::Taken) {
159+
let b_val = match std::mem::replace(&mut this.b, MaybeDone::Taken) {
152160
MaybeDone::Done(v) => v,
153161
_ => unreachable!(),
154162
};
@@ -159,10 +167,10 @@ where
159167
}
160168
}
161169

162-
// Usage:
170+
// Usage (async blocks are !Unpin, so wrap them with Box::pin):
163171
// let (page1, page2) = Join::new(
164-
// http_get("https://example.com/a"),
165-
// http_get("https://example.com/b"),
172+
// Box::pin(http_get("https://example.com/a")),
173+
// Box::pin(http_get("https://example.com/b")),
166174
// ).await;
167175
// Both requests run concurrently!
168176
```

0 commit comments

Comments
 (0)