Search code examples
rustasync-awaitrust-tokiorust-futuresrust-async-std

Implement Future trait based on future available inside the struct


I'm trying to create a DelayedValue future that resolves to a value after a certain time period has elapsed. To do this I simply wanted to wrap the Sleep future from tokio crate. But I get errors relating to Pin and no matter what I do I can't seem to call the poll method on the underlying Sleep member.

For reference here is a full program which fails to compile but should illustrate what I want:

use futures::task::{Context, Poll};
use futures::Future;
use std::pin::Pin;
use tokio::time::{sleep, Sleep, Duration};

struct DelayedValue<T> {
    value: T,
    sleep: Sleep,
}

impl<T> Future for DelayedValue<T> {
    type Output = T;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        match &mut self.sleep.poll(cx) {
            Poll::Ready(()) => Poll::Ready(self.value),
            x => x,
        }
    }
}

#[tokio::main]
async fn main() {
    let dv = DelayedValue {
        value: 10_u8,
        sleep: sleep(Duration::from_millis(5000)),
    };

    println!("waiting for delayed value");
    
    let v = dv.await;
    println!("delayed value: {}", v);
}

There is also a playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d573d8dcbbef5c99314d98cacc3d6c92


Solution

  • The easiest way is to use pin-project or pin-project-lite:

    pin_project_lite::pin_project! {
        struct DelayedValue<T> {
            value: Option<T>,
            #[pin]
            sleep: Sleep,
        }
    }
    
    impl<T> Future for DelayedValue<T> {
        type Output = T;
    
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.project();
            match this.sleep.poll(cx) {
                Poll::Ready(()) => Poll::Ready(this.value.take().unwrap()),
                Poll::Pending => Poll::Pending,
            }
        }
    }