Understanding the trait is key to mastering async Rust.
rustpub trait Future { type Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>; } pub enum Poll<T> { Ready(T), Pending, }
How Polling Works
- An Executor calls
poll()on your Future - If ready, returns
Poll::Ready(value) - If not ready:
- Registers a
Wakerwith the async resource - Returns
Poll::Pending
- Registers a
- When the resource completes, it calls
waker.wake() - The Executor polls again
The Waker Mechanism
The Waker is how async I/O notifies the executor that progress can be made.
rustimpl Future for MyTimer { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { if self.is_ready() { Poll::Ready(()) } else { // Register the waker so we get polled again self.register_waker(cx.waker().clone()); Poll::Pending } } }
Why Pin?
Async functions compile to state machines that may contain self-references. Pin prevents moving the Future in memory, which would invalidate those references.
rustasync fn example() { let data = vec![1, 2, 3]; let reference = &data[0]; // Self-reference! some_async_op().await; // State machine stores both println!("{reference}"); }
See The Future Trait.
Code Examples
rust
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
// A Future that's immediately ready
struct Ready<T>(Option<T>);
impl<T> Future for Ready<T> {
type Output = T;
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<T> {
Poll::Ready(self.0.take().unwrap())
}
}
// Usage
async fn example() {
let value = Ready(Some(42)).await;
assert_eq!(value, 42);
}