Search code examples
asynchronousrustrust-futures

impl Future for struct with Future in Rust


I'm working on exercises from the Rust playground, and I'm stuck on implementing a Future for a structure. Here's how the task looks:

//Provide a Future trait implementation, transparently polling the inner_future,
// and printing its execution time in nanoseconds once it's ready.
// Using Fut: Unpin trait bound (or similar) is not allowed.

struct Measurable<Fut> {
    inner_future: Fut,
    started_at: Instant,
}

The task seems clear. We need to create a wrapper around Future and print its execution time. However, it's unclear what Fut is. I assumed it's a Future and drafted the following code:

use futures::executor::block_on;
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
    time::Duration,
    time::Instant,
};

#[derive(Debug)]
struct Measurable<Fut> {
    inner_future: Fut,
    started_at: Instant,
}

impl<Fut> Measurable<Fut> {
    fn new(inner_future: Fut) -> Self {
        MeasurableFuture {
            inner_future,
            started_at: Instant::now(),
        }
    }
}

impl<Fut: Future> Future for Measurable<Fut> {
    type Output = Fut::Output;

    fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
        // move occurs because value has type `Fut`, which does not implement the `Copy` trait
        let res = Poll::Ready(self.inner_future);

        match res {
            // syntax says that 'result' is a Fut, should i return Fut or Fut::Output?
            Poll::Ready(result) => {
                println!(
                    "Completed: {:?}",
                    Instant::now().checked_duration_since(self.started_at)
                );
                result
                // Poll::Pending
            }
            Poll::Pending => Poll::Pending,
        }
    }
}

async fn hello_world() {
    std::thread::sleep(Duration::from_secs(1));
    println!("hello, world!");
}

fn main() {
    let w = Measurable::new(hello_world());
    let result = block_on(w);
    println!("{:?}", result);
}

So there are two problems:

  1. Move occurs because value has type Fut, which does not implement the Copy trait

    res = Poll::Ready(self.inner_future);

  2. I don't know what I should to return when task is ready

The question is, how do I correctly implement poll for this case, and what should I return? Maybe I misunderstood the task.


Solution

  • To create a Pin<&mut Field> safely, you can use the pin-project or pin-project-lite crates:

    pin_project_lite::pin_project! {
        #[derive(Debug)]
        struct Measurable<Fut> {
            #[pin]
            inner_future: Fut,
            started_at: Instant,
        }
    }
    
    impl<Fut: Future> Future for Measurable<Fut> {
        type Output = Fut::Output;
    
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            let this = self.project();
    
            let res = this.inner_future.poll(cx);
    
            match res {
                Poll::Ready(result) => {
                    println!(
                        "Completed: {:?}",
                        Instant::now().checked_duration_since(*this.started_at)
                    );
                    Poll::Ready(result)
                }
                Poll::Pending => Poll::Pending,
            }
        }
    }
    

    You can also do that using unsafe code, but I wouldn't recommend that.