Rust
Future trait
Learn the Future trait's poll mechanism.
The Future trait
Behind async fn there is a trait with a single method:
pub trait Future {
type Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}
pub enum Poll<T> {
Ready(T),
Pending,
}
poll either returns the result (Ready) or says it isn't ready yet (Pending). A future returning Pending promises to wake the executor via cx.waker() when it's ready.
A hand-written future
A future that returns Pending a few times and then becomes ready:
struct Geri {
kalan: u32,
}
impl Future for Geri {
type Output = &'static str;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.kalan == 0 {
return Poll::Ready("bitti");
}
self.kalan -= 1;
cx.waker().wake_by_ref(); // "poll me again"
Poll::Pending
}
}
Since Geri's fields are Unpin, the fields can be accessed directly through Pin<&mut Self>.
Exercise Counting executor
Write the Geri future above. Change block_on into calistir, which returns the result together with how many times it polled. Print both for Geri { kalan: 3 }, separated by a space.
Show example solution
use std::future::Future;
use std::pin::{pin, Pin};
use std::task::{Context, Poll, Waker};
struct Geri {
kalan: u32,
}
impl Future for Geri {
type Output = &'static str;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.kalan == 0 {
return Poll::Ready("bitti");
}
self.kalan -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
fn calistir<F: Future>(f: F) -> (F::Output, u32) {
let mut f = pin!(f);
let mut cx = Context::from_waker(Waker::noop());
let mut sayi = 0;
loop {
sayi += 1;
if let Poll::Ready(v) = f.as_mut().poll(&mut cx) {
return (v, sayi);
}
}
}
fn main() {
let (sonuc, n) = calistir(Geri { kalan: 3 });
println!("{} {}", sonuc, n);
}
Test yourself
-
What happens in a real runtime if a future returns <code>Pending</code> and never calls the Waker?
- The runtime retries
- Panic
- The result becomes None
- The task is never polled again and hangs forever
Show answer
The task is never polled again and hangs forever — The wake-up contract is mandatory; it's the most common mistake when writing your own future.
-
Not ready yet
self.kalan -= 1;
cx.waker().wake_by_ref();
Poll::____
Show answer
self.kalan -= 1;
cx.waker().wake_by_ref();
Poll::Pending
-
Item and role
- Poll::Ready(v)
- Result ready
- Poll::Pending
- Later
- cx.waker()
- Waker
- type Output
- Result type
Next lesson: async block