Implement Future trait based on future available inside the struct

1.3k Views Asked by At

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

3

There are 3 best solutions below

3
On BEST ANSWER

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,
        }
    }
}
0
On

For reference, since I only needed this for this struct I opted to not use pin-project. Instead I implemented it myself for the field I needed:

#[derive(Debug)]
pub struct DelayedValue<T: Copy> {
    value: T,
    sleep: Sleep,
}

impl<T: Copy> DelayedValue<T> {
    pub fn new(value: T, sleep: Sleep) -> DelayedValue<T> {
        DelayedValue {value, sleep}
    }
}

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

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let x = self.value;
        let s = unsafe { self.map_unchecked_mut(|s| &mut s.sleep) };

        match &mut s.poll(cx) {
            Poll::Ready(()) => Poll::Ready(x),
            Poll::Pending => Poll::Pending,
        }
    }
}

0
On

The easiest is probably to just map the result of a Sleep future instead of implementing a full new struct:

use futures::FutureExt;
use tokio::time::{ Duration, sleep };

#[tokio::main]
async fn main() {
    let dv = sleep (Duration::from_millis (5000)).map (|_| { 10_u8 });

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

Playground