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,
}
}
}
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) {
...
}
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.