0

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

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
John Smith
  • 179
  • 4

3 Answers3

2

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,
        }
    }
}
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • Thank you! This certainly works. But I would be very interested what's going on here. If you can could you please explain a bit more? Or link to docs or blog post that covers this? – John Smith Nov 27 '22 at 00:17
  • @JohnSmith I linked to the docs of the crates. These crates allow you to project a `Pin<&mut Struct>` into `Pin<&mut Field>`s or `&mut Field`s, depending on whether you choose the field to be `#[pin]` or not. – Chayim Friedman Nov 27 '22 at 00:26
  • What I was looking for is this: https://doc.rust-lang.org/nightly/core/pin/index.html#projections-and-structural-pinning – John Smith Nov 27 '22 at 00:35
0

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

John Smith
  • 179
  • 4
0

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

Jmb
  • 18,893
  • 2
  • 28
  • 55