No method named `poll` found for a type that implements `Future`
Asked Answered
A

1

16

I am attempting to create a struct that will allow someone to call .shutdown(), which will resolve a future (that is otherwise pending). It can only be called once. In the implementation of the Future trait, I receive an error that poll is not defined, despite it being present in the documentation (under impl Future).

Though I am using std::future::Future as the impl, I tried adding use futures::prelude::*, which would bring the preview trait into scope. Both RLS and rustc inform me that the import is unused, so that's not the issue.

Note that I am not using a simple boolean flag, as I intend for this to be able to be callable from any thread — that's an implementation detail that is irrelevant here.

use futures::channel::oneshot; // [email protected]
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

pub struct ShutdownHandle {
    sender: oneshot::Sender<()>,
    receiver: oneshot::Receiver<()>,
}

impl ShutdownHandle {
    pub fn new() -> Self {
        let (sender, receiver) = oneshot::channel();
        Self { sender, receiver }
    }

    pub fn shutdown(self) -> Result<(), ()> {
        self.sender.send(())
    }
}

impl Future for ShutdownHandle {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        self.receiver.poll(&mut cx).map(|_| ())
    }
}

fn main() {
    let runner = ShutdownHandle::new();
    assert!(runner.shutdown().is_ok());
}

I receive the following error:

error[E0599]: no method named `poll` found for type `futures_channel::oneshot::Receiver<()>` in the current scope
  --> src/main.rs:28:23
   |
28 |         self.receiver.poll(&mut cx).map(|_| ())
   |                       ^^^^

What am I missing? Surely there's some way to "pass through" the polling. I am using nightly (2019-07-18).

Appurtenant answered 6/8, 2019 at 4:49 Comment(0)
F
13

It's true, Receiver does not implement Future; only Pin<&mut Receiver> does. You need to project the pinning from your type to the field.

When the underlying type may not implement Unpin

impl Future for ShutdownHandle {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        // I copied this code from Stack Overflow without reading the text that
        // told me how to verify that this code uses `unsafe` correctly.
        unsafe { self.map_unchecked_mut(|s| &mut s.receiver) }.poll(cx).map(|_| ())
    }
}

You must read the pin module to thoroughly understand the requirements to use unsafe here.

A cleaner solution

I like to use a helper library, such as pin_project, to handle more complicated types of projection:

#[unsafe_project(Unpin)]
pub struct ShutdownHandle {
    #[pin]
    sender: oneshot::Sender<()>,
    #[pin]
    receiver: oneshot::Receiver<()>,
}

impl Future for ShutdownHandle {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        let this = self.project();
        this.receiver.poll(cx).map(|_| ())
    }
}

When the underlying type implements Unpin

Ömer Erden points out that the futures-preview crate provides FutureExt::poll_unpin. This method takes a mutable reference to a type that implements Unpin and creates a brand new Pin with it.

Since oneshot::Receiver does implement Unpin, this can be used here:

impl Future for ShutdownHandle {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
        self.receiver.poll_unpin(cx).map(|_| ())
    }
}

See also

Francklyn answered 6/8, 2019 at 13:49 Comment(3)
It's true, Receiver does not implement Future; only Pin<&mut Receiver> does. In this case using poll_unpin would be a quicker solution but not sure is there any pitfall using poll_unpin or not. I would like to know if it is applicable for this problem ?Oriana
@ÖmerErden In this specific situation, that certainly seems to be the simpler solution, and avoids using unsafe (at least directly). Thanks for that!Appurtenant
For future reference for myself and anyone else: Keep in mind that you may get this error even if you're implementing the trait, as there's currently a transition from 0.1 to 0.3 Futures. Hopefully this transition period will be relatively short, but this is due to the same reason linked in the answer.Appurtenant

© 2022 - 2024 — McMap. All rights reserved.