CodeBlanks Logo Icon CodeBlanks

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>.

Practice 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.

Starter code

main.rs
use std::future::Future;
use std::pin::{pin, Pin};
use std::task::{Context, Poll, Waker};

fn main() {
    // buraya yaz
}

Sample solution

main.rs
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);
}

Expected output

bitti 4

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 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 Correct answer

Explanation: The wake-up contract is mandatory; it's the most common mistake when writing your own future.

2 Not ready yet

Question
self.kalan -= 1;
cx.waker().wake_by_ref();
Poll::____
Answer
self.kalan -= 1;
cx.waker().wake_by_ref();
Poll::Pending

Explanation: Pending reports that the result isn't ready yet.

3 Item and role

Term Meaning
Poll::Ready(v) Result ready
Poll::Pending Later
cx.waker() Waker
type Output Result type

All lessons in Advanced