简体   繁体   中英

In async Rust, how to I implement a wrapper around poll?

Suppose I have an async function foo that returns some value. The value returned is different after each call (this could be eg something that reads chunks of data from a file or a random number generator).

I want to implement a wrapper struct around foo that implements AsyncRead . The wrapper reads the values from foo , processes them in some way, and then places them into the user-supplied buffer.

Here's what I've tried:

use futures::io::AsyncRead;
use pin_utils::pin_mut;
use std::io;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;

async fn foo() -> u8 {
    b'x'
}

pub struct FooReader;

impl AsyncRead for FooReader {
    fn poll_read(self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut [u8])
        -> Poll<io::Result<usize>> 
    {
        if buf.is_empty() {
            return Poll::Ready(Ok(0));
        }
        match foo().poll() {  // << problem here
            Poll::Ready(byte) => {
                buf[0] = byte;
                Poll::Ready(Ok(1))
            },
            Poll::Pending => Poll::Pending,
        }
    }
}

(Link to Playground)

This obviously doesn't compile because poll() wants a pinned value. Now, how do I pin the future returned by foo ? I tried various combinations of Box::pin , Pin::new and pin_mut! but none of them worked.

Also, am I supposed to store the Future in my struct FooReader until it's ready? Or am I free to drop it after each call, even if it's pending?


EDIT: The following works:

let mut future: Pin<Box<dyn Future<Output = u8>>> = Box::pin(foo());
match future.as_mut().poll(ctx) {
    ...
}

(Link to Playground)

For some reason I have to give future an explicit type annotation, otherwise it doesn't compile (somehow the compiler is making confusion between impl Future and dyn Future ).

Even if it works, I would still like to know if this is the "official" way to do it.

For some reason, the Future trait is not in the standard prelude, so you have to use it before calling the function (the hint is there, buried between all the other compiler errors):

use std::future::Future;

Then, yes, you must pin the future before use. The easiest way in your case is with pin_mut! , that uses a funny syntax:

let f = foo();
pin_mut!(f);

Now, you can call poll() , but remember that you must forward your Context argument:

match f.poll(ctx) {

Playground with working code.

About storing the future or dropping it, you can do whatever you want, that depends on the exact semantics. I personally would expect you to keep the future and run it to completion unless there is something in the documentation that explicitly says otherwise.

Naturally, if you store the future in your struct, pin_mut! will not work because it consumes its argument. You need to either keep it in a Pin<Box> , or project the pin from self , since it is already pinned.

To keep the Pin<Box> you do something like:

pub struct FooReader {
    f: Option<Pin<Box<dyn Future<Output=u8>>>>,
}

And then to use it do not forget the Box::as_mut() since you need a Pin<&mut Future> , not a Pin<Box<impl Future>> :

        let f = foo();
        self.f = Some(Box::pin(f)); //remember to declare (mut self) above!
        match self.f.as_mut().unwrap().as_mut().poll(ctx) {

You'll probably want to check self.f before calling foo() again, but you get the idea.

The option of the pin projection is nicer because you avoid the extra allocation for the Box , but it is more complex and requires extra crates or a bit of unsafe code. If you are really interested I think it deserves a new question on its own.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM