# \[Idea\] Future fast poll api

**URL:** <https://internals.rust-lang.org/t/idea-future-fast-poll-api/23401>\
**Category:** language design\
**Created:** [August 15, 2025, 4:10pm UTC](https://internals.rust-lang.org/t/idea-future-fast-poll-api/23401 "2025-08-15T16:10:08Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Qsh](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/qsh/32/10418_2.png) [@Qsh](https://internals.rust-lang.org/u/Qsh)\
**Post date:** [August 15, 2025, 4:10pm UTC](https://internals.rust-lang.org/t/idea-future-fast-poll-api/23401/1 "2025-08-15T16:10:08Z")

</div>

Typically, Rust compiles asynchronous functions into state machines. However, each time this state machine is polled, it needs to be invoked from the top level sequentially, which results in poor performance in certain continuous polling implementations of async functions.

(This might be considered an anti-pattern, but can be useful in some embedded development scenarios.)

For example:

```rust
pub async fn next_poll() {
    let mut once = true;
    core::future::poll_fn(|cx| {
        if core::mem::take(&mut once) {
            cx.waker().wake_by_ref();
            Poll::Pending
        } else {
            Poll::Ready(())
        }
    })
    .await
}

```

There are some attempts, Such as: [https://internals.rust-lang.org/t/pre-rfc-cps-transform-for-generators/7120](https://internals.rust-lang.org/t/pre-rfc-cps-transform-for-generators/7120)

Can we enable the async runtime to have a fast-path for advancing the state machine's leaf nodes after encountering `Pending` by erasing the return value?

```rust
use std::pin::Pin;
use std::task::{Context, Poll};

pub trait Block {
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool;
}

pub struct FutureBlock<F: Future> {
    future: F,
    ready: Option<F::Output>,
}

impl<F: Future> FutureBlock<F> {
    pub fn future(self: Pin<&mut Self>) -> Pin<&mut F> {
        unsafe { self.map_unchecked_mut(|s| &mut s.future) }
    }

    pub fn ready(self: Pin<&mut Self>) -> &mut Option<F::Output> {
        unsafe { &mut self.get_unchecked_mut().ready }
    }
}

impl<F: Future> Block for FutureBlock<F> {
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> bool {
        if self.ready.is_some() {
            true
        } else {
            unsafe {
                match self.as_mut().future().poll(cx) {
                    Poll::Ready(ready) => {
                        *self.ready() = Some(ready);
                        true
                    }
                    Poll::Pending => false,
                }
            }
        }
    }
}

impl<F: Future> Future for FutureBlock<F> {
    type Output = F::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if let Some(ready) = self.as_mut().ready().take() {
            Poll::Ready(ready)
        } else {
            self.future().poll(cx)
        }
    }

    fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
        Some(self)
    }
}

pub trait Future {
    type Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;

    fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
        None
    }
}

impl<P> Future for Pin<P>
where
    P: std::ops::DerefMut<Target: std::future::Future>,
{
    type Output = <<P as std::ops::Deref>::Target as std::future::Future>::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        <P::Target as std::future::Future>::poll(self.as_deref_mut(), cx)
    }

    fn get_block(self: Pin<&mut Self>) -> Option<Pin<&mut dyn Block>> {
        None
    }
}

```
