Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

rust

future

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);
    });
}
like image 483
Anunaki Avatar asked Nov 16 '22 01:11

Anunaki


1 Answers

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.

like image 69
boxdot Avatar answered Dec 16 '22 06:12

boxdot