Implementors of `Future`

โš“ Rust    ๐Ÿ“… 2025-08-26    ๐Ÿ‘ค surdeus    ๐Ÿ‘๏ธ 3      

surdeus

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Implementors of `Future`

After reading Boats' "Why Async Rust" I've come to notice these (std (permalink)):

#[stable(feature = "futures_api", since = "1.36.0")]
impl<F: ?Sized + Future + Unpin> Future for &mut F {
    type Output = F::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        F::poll(Pin::new(&mut **self), cx)
    }
}
#[stable(feature = "futures_api", since = "1.36.0")]
impl<P> Future for Pin<P>
where
    P: ops::DerefMut<Target: Future>,
{
    type Output = <<P as ops::Deref>::Target as Future>::Output;

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

Specifically I'm interested in the reason behind the F: Unpin trait bound for &mut F to be Future. Is it to guard against something like Arc<Mutex<F: !Unpin + Send + Future>> (playground)?

use std::{
    future::poll_fn, hint::spin_loop, mem::replace, pin::Pin, sync::Arc, task::Poll, time::Duration,
};
use tokio::{
    join, spawn as task_spawn, sync::Mutex, sync::oneshot::channel, task::spawn_blocking,
    time::sleep,
};

#[tokio::main]
async fn main() {
    let fut = Arc::new(Mutex::new(sleep(Duration::from_secs(2))));

    let h0 = {
        let fut = Arc::clone(&fut);
        task_spawn(async move {
            let fut = &mut *fut.lock().await;
            let fut: &mut (dyn Future<Output = ()> + Send) = fut;
            let fut = unsafe {
                // Supposedly the reason why we need `Unpin` trait bound?
                // safety:
                // _not_ safe since it's not local-pinning but some arbitrary reference
                // in particular it's behind `Arc<Mutex<_>>` s.t. other thread may do bad things
                Pin::new_unchecked(fut)
            };
            fut.await;
        })
    };

    let (tx, rx) = channel();
    let mut new_nap = sleep(Duration::from_secs(1));
    poll_fn(|cx| {
        // Stolen from https://fasterthanli.me/articles/pin-and-suffering
        // abusing `tokio::time::Sleep` for demonstration purposes
        //
        // There might exist some other sorts of `!Unpin` `Future` that's even worse?
        // in the sense simply `mem::swap` a fresh, not yet `poll`-ed instance is enough to
        // break certain invariants within the type
        unsafe {
            _ = Pin::new_unchecked(&mut new_nap).as_mut().poll(cx);
        };
        Poll::Ready(())
    })
    .await;
    let h1 = spawn_blocking(move || {
        loop {
            match fut.try_lock() {
                Ok(mut old_nap) => {
                    tx.send(replace(&mut *old_nap, new_nap)).unwrap();
                    break;
                }
                Err(_) => {
                    spin_loop();
                }
            }
        }
    });
    let old_nap = task_spawn(async move { rx.await.unwrap().await });

    _ = join!(h0, h1, old_nap);
}

If that's the case, then I don't understand this quote: I'm assuming &mut dyn Future trait objects here, and from the above experiment, we can see that indeed we do need the Unpin trait bound, no?

The only one thatโ€™s really bad... is that you need to pin a future trait object to await it

3 posts - 2 participants

Read full topic

๐Ÿท๏ธ Rust_feed