We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Async From First Principles step 3 of 25
The `Future` trait: `Output`, `Poll`, and your first hand-written future
async fn, .await, tokio, streams — every one of them compiles down to one
trait. Here it is, verbatim, as it has been in std since Rust 1.36:
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
Write two implementations of it by hand, then look at what they do.
pub struct Immediate<T>(pub Option<T>); // already finished
pub struct NeverDone; // never finishes
pub fn probe(vals: Vec<i32>, poll_never: bool) -> Vec<Option<i32>>
probe polls one Immediate(Some(v)) once for every value in vals,
recording Some(v) when the poll returns Ready and None when it returns
Pending. If poll_never is true it then polls one NeverDone once and
records that too. Immediate is always Ready; NeverDone is always
Pending; so the output is vals with an optional null on the end.
How to read poll
Read the signature as a sentence: “try to make progress without blocking, and tell me whether you finished.” It returns
enum Poll<T> { Ready(T), Pending }
Poll lives in std::task, not std::future — importing it from the wrong
module is the single most common first-minute mistake here.
Two parameters deserve a note and then deliberate silence:
-
self: Pin<&mut Self>— a wrapper that promises the future will not be moved in memory once polling starts. Both of the types here hold nothing address-sensitive, so they areUnpinandself.0just works. Item 16.10 explains whatPinis actually for, once you have written enough futures to have the question. -
cx: &mut Context<'_>— the “how to wake me up” handle. Ignore it in this problem; you never returnPendingfor a real reason yet. Items 16.6 and 16.7 are entirely about it.
Building a Context out of nothing
To poll a future you need a Context, and to build a Context you need a
Waker. std ships a do-nothing one:
let mut cx = Context::from_waker(Waker::noop());
Waker::noop() (stable since 1.85) returns a &'static Waker, so there is
no & in front of it. Adding one is the classic error.
Polling a future by hand
poll takes Pin<&mut Self>, not &mut self. For a type that is Unpin,
Pin::new(&mut fut) builds one safely and for free:
let mut fut = Immediate(Some(7));
let p = Pin::new(&mut fut).poll(&mut cx);
The error you are meant to meet
The starter’s impl Future for Immediate<T> is missing one line. The compiler
answers with E0046: “not all trait items implemented, missing: Output“.
A trait with an associated type is not satisfied until you say what that type
is — and Output is the whole reason the trait exists. Immediate<T>
produces a T; NeverDone never produces anything, but it still has to
declare a type it would have produced.
You will also see E0277 here if you get a type wrong, because .poll() is
only callable on something that implements Future.
One deliberate sharp edge
Immediate hands its value out with Option::take, so polling it a second
time panics. That is not a bug: the documented contract says a future must not
be polled after it has returned Ready, and panicking is a perfectly
respectable way to enforce a contract. Real combinators guard this with an
if slot.is_none() check instead — you will write exactly that in item 16.14.
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.