We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Async From First Principles step 5 of 25
Countdown futures and the shape of Pending
Implement a future that counts how many times it is polled. For each value
in script, create a fresh Countdown, run it with the supplied block_on,
and collect its result in input order.
pub fn poll_trace(script: Vec<u32>) -> Vec<u32>
Countdown behavior
Countdown::new(n) starts with left = n and polls = 0. Each call to
poll must:
- Increment
polls. - If
leftis zero, returnPoll::Ready(polls). - Otherwise decrement
left, callcx.waker().wake_by_ref(), and returnPoll::Pending.
This produces exactly n Pending results followed by Ready(n + 1). For n = 2, the three calls return Pending, Pending, then Ready(3). For n = 0, the first call returns Ready(1).
An empty script returns an empty vector. For example, [0, 3, 1] produces
[1, 4, 2]. Keep the supplied driver and implement the Future behavior rather
than replacing the exercise with direct addition.
Inputs are in 0..=99_999: the driver allows at most 100,000 polls for each
future. A countdown of 99,999 finishes on the final allowed poll. A countdown
of 100,000 would still be Pending at that point, so the driver panics.
The limit applies separately to each countdown, not to the entire script.
Pending does not schedule itself
poll should return promptly without blocking. Pending means the result is
not ready; returning it does not automatically arrange another poll.
A future must arrange an appropriate wakeup when it can make progress.
This artificial countdown can make progress immediately on its next poll, so it wakes its own task before returning Pending. A wakeup requests another opportunity to poll; it does not synchronously call poll or guarantee that the next result will be Ready. Real I/O futures usually arrange a wakeup when an external event occurs instead of repeatedly waking themselves.
The supplied teaching driver uses Waker::noop() and polls in a tight loop.
It does not wait for wakeups, so its output tests cannot detect a missing
wake_by_ref call. The call is still required by this exercise. This driver
is deliberately limited and is not a general-purpose async executor.
Stop polling a future after Ready. The Future trait does not promise a useful result from polling a completed future again.
State must survive between calls
In this handwritten poll method, each call starts at the beginning. Keep
left and polls in the Countdown fields so their updated values survive
the return. A local counter initialized inside poll would reset each time.
Check for zero before subtracting. Subtracting one from zero as u32 can panic
when overflow checks are enabled or wrap to 4,294,967,295 when they are not.
The daemon currently builds with rustc -O, whose default disables these
checks; an explicit compiler option can enable them even in optimized code.
Correct zero handling works in both configurations.
Mutating through Pin
The Future trait takes self: Pin<&mut Self>. Countdown contains only u32
fields, so it automatically implements Unpin. That permits safe mutable
access through this pinned reference, including updates to both counters.
No unsafe code or custom pin projection is needed for this type.
Pin has different implications for types that are not Unpin. The fact that these field updates compile does not mean pinning can always be ignored.
Iterate over values
The starter indexes script through a range loop. Clippy can flag that pattern
with needless_range_loop. Iterate over script's values directly, or use an
iterator with map and collect. Submissions must compile, pass the output tests
and pass Clippy with warnings treated as errors.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.