How to call an async function in poll() method? [duplicate]
Asked Answered
C

2

7

I want to call async function in Future::poll(), but the poll() is not async function, so I have to poll the async fn, but got error:

error[E0599]: no method named `poll` found for opaque type `impl Future` in the current scope
  --> src\lib.rs:18:22
   |
18 |         let a = fut1.poll(cx);
   |                      ^^^^ method not found in `impl Future`

I try to Pin::new(async fn()).poll(), got another error:

error[E0277]: `from_generator::GenFuture<[static generator@src\lib.rs:33:23: 36:2 {ResumeTy, u64, Duration, impl Future, ()}]>` cannot be unpinned
  --> src\lib.rs:22:23
   |
22 |         let pinfut1 = Pin::new(&mut fut1);
   |                       ^^^^^^^^ within `impl Future`, the trait `Unpin` is not implemented for `from_generator::GenFuture<[static generator@src\lib.rs:33:23: 36:2 {ResumeTy, u64, Duration, impl Future, ()}]>`
...
33 | async fn fn1() -> i32 {
   |                   --- within this `impl Future`

code:

use std::future::Future;
use std::task::{Context, Poll};
use std::pin::Pin;
use std::sync::{Arc,Mutex};

#[pin_project::pin_project]
struct Person<'a> {
    name: &'a str,
    age: i32,
}

impl<'a> Future for Person<'a> {
    type Output = i32;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.project();
        *this.age += 1;

        // error: poll() ^^^^ method not found in `impl Future`
        // let mut fut1 = fn1();
        // let a = fut1.poll(cx);

        // error: Pin::new() ^^^^^^^^ within `impl Future`, the trait `Unpin` is not implemented for `from_generator::GenFuture
        let mut fut1 = fn1();
        let pinfut1 = Pin::new(&mut fut1);
        let a = pinfut1.poll(cx);

        if *this.age > 4 {
            Poll::Ready(*this.age)
        } else {
            Poll::Pending
        }
    }
}

async fn fn1() -> i32 {
    async_std::task::sleep(std::time::Duration::from_secs(2)).await;
    123
}

fn main() {
    let p1 = Person {name: "jack", age: Default::default()};
    async_std::task::block_on(async {
        let a = p1.await;
        dbg!(a);
    });
}
Cooncan answered 11/1, 2021 at 7:44 Comment(0)
N
5

To call poll on fut1 which is returned by fn1, you need to obtain a Pin<&mut F>, where F is the type of the future fut1. There are at least three possibilities to do this (I omit unsafe):

  1. If fut1 implements Unpin, you can call Pin::new,
  2. you can pin fut1 on the heap by using Box::pin, or
  3. you can pin fut1 on the stack by using the futures_util::pin_mut! macro.

It is sometimes a little bit hard to figure out whether Unpin is implemented, and if not, then why. A general advice here is to desugar the signature of fn1 into: fn fn1() -> impl Future<Output = usize> + Unpin. The compiler errors are usually more precise then.

The implementation of the pin_mut! macro has some useful insights, btw. It uses the Rust type system to move the future and shadows the variable, s.t. it cannot be accessed anymore, and by doing this, it guarantees that the invariants of Pin::new_unchecked are satisfied and therefore it can call it.

Nikkinikkie answered 11/1, 2021 at 10:6 Comment(3)
When I change my async fn1() to: fn fn1() -> impl Future<Output=i32> + Unpin, got error: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within impl Future, the trait Unpin is not implemented for from_generator::GenFuture<[static generator@src/main.rs:29:11: 34:6]>Cooncan
@Anunaki: Yes, this is also what I get. It just means that fut1 does not implement Unpin.Nikkinikkie
fn fn1() -> impl Future<Output=i32> + Unpin {async {}.boxed()} is ok, and then Pin::new() can works, this does not require Pin::new_unchecked(). but have other problem in myself reply , code to see post floor #2Cooncan
C
-4

I have changed code to following, complie ok, but result incorrect: the async fn will be called by poll way in Future::poll(), but the .await statement in the async fn will be blocked forever.

use std::future::Future;
use std::task::{Context, Poll};
use std::pin::Pin;
use std::sync::{Arc,Mutex};
use futures::FutureExt;

#[pin_project::pin_project]
struct Person<'a> {
    name: &'a str,
    age: i32,
}

impl<'a> Future for Person<'a> {
    type Output = i32;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let mut this = self.project();
        *this.age += 10;

        let mut fut1 = fn1();
        let pinfut1 =  Pin::new(&mut fut1);
        //let pinfut1 = unsafe { Pin::new_unchecked(&mut fut1) };
        pinfut1.poll(cx)
    }
}

fn fn1() -> impl Future<Output=i32> + Unpin {
    async {
        dbg!("sleep start!"); // execute here!
        async_std::task::sleep(std::time::Duration::from_secs(5)).await; //  <--- blocked here ?
        dbg!("sleep done!"); // Never execute here!
        123
    }.boxed()
}

fn main() {
    let p1 = Person {name: "jack", age: Default::default()};
    async_std::task::block_on(async {
        let a = p1.await;
        dbg!(a); // Never execute here! // expect 123
    });
    std::thread::park();
}

Cooncan answered 11/1, 2021 at 7:55 Comment(1)
What guarantees need to be made to ensure that this unsafe block is safe? Is it safe in general? Or is it only safe for this example?Autum

© 2022 - 2024 — McMap. All rights reserved.