0 day streak
rust / expert
Snippet

Implementing a Custom Manual Future Poller

This snippet demonstrates how to manually implement and execute a Future without using async/await syntax. It constructs a dummy Waker using a custom virtual table (RawWakerVTable) and polls a stateful countdown future within a manual execution loop.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
static VTABLE: RawWakerVTable = RawWakerVTable::new(
|_| RawWaker::new(std::ptr::null(), &VTABLE),
|_| {},
|_| {},
|_| {},
);
struct CountdownFuture {
count: usize,
}
impl Future for CountdownFuture {
type Output = String;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.count == 0 {
Poll::Ready("Finished!".to_string())
} else {
self.count -= 1;
cx.waker().wake_by_ref();
Poll::Pending
}
}
}
fn execute_future() -> String {
let mut fut = CountdownFuture { count: 3 };
let mut pinned = Pin::new(&mut fut);
let raw_waker = RawWaker::new(std::ptr::null(), &VTABLE);
let waker = unsafe { Waker::from_raw(raw_waker) };
let mut cx = Context::from_waker(&waker);
loop {
match pinned.as_mut().poll(&mut cx) {
Poll::Ready(result) => return result,
Poll::Pending => {}
}
}
}
Breakdown
1
static VTABLE: RawWakerVTable = ...
Defines a no-op virtual table for the custom raw waker to fulfill the task interface requirement.
2
cx.waker().wake_by_ref();
Instructs the executor that the task is ready to be polled again immediately by waking its ref.
3
let waker = unsafe { Waker::from_raw(raw_waker) };
Reconstitutes a safe Waker instance from our raw waker pointer structure.
4
loop { match pinned.as_mut().poll(&mut cx) { ... } }
Drives the future to completion by calling poll inside a standard control flow loop.