3

I am trying to wrap AsyncRead in another AsyncRead (and do some data processing on it). However when trying to store the Future of .read() I get lifetime problem: self has an anonymous lifetime '_ but it needs to satisfy a 'static lifetime requirement.

Code:

pub struct AsyncReadWrap {
    input: Pin<Box<dyn AsyncRead + 'static>>,
    future: Option<Pin<Box<dyn Future<Output = std::io::Result<usize>>>>>
}

impl AsyncReadWrap {
    pub fn new(input: impl AsyncRead + Unpin + 'static) -> AsyncReadWrap {
        AsyncReadWrap {
            input: Box::pin(input),
            future: None
        }
    }
}

impl AsyncRead for AsyncReadWrap {
    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<std::io::Result<()>> {
        
        let mut buffer = [0u8; 2048];
        let future = self.input.as_mut().read(&mut buffer);
        self.future = Some(Box::pin(future));
       
        Poll::Pending
    }
}

I am new to async and it's weird that there isn't some simple way to await in poll functions. Thank you.

Makalone LOgman
  • 948
  • 1
  • 12
  • 25

1 Answers1

2

You cannot call await from a poll function, because await may call poll muliple times and yield control back to the executor in between. A single poll call may only yield once, and will only get resumed through another poll invocation. Polling and futures is the building block of async/await - you cannot use the higher level abstraction when working with the lower level implementation.

The problem with your current code is that your struct is self-referential. input.read() returns a future the may borrow from input:

// `Read` borrows from `self` and `buf`
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self> 

You cannot store the returned future in your struct because storing a value and a reference to that value in the same struct is problematic.

To create an AsyncRead wrapper, you can call poll_read on the inner AsyncRead. When poll_read returns Ready, the buffer will be filled with the read value, and you can perform data processing on it:

impl AsyncRead for AsyncReadWrap {
    fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, mut buf: &mut ReadBuf<'_>) -> Poll<std::io::Result<()>> {
        match self.input.as_mut().poll_read(cx, &mut buf) {
            Poll::Ready(res) => {
                // do stuff with `buf`
                Poll::Ready(res)
            },
            Poll::Pending => Poll::Pending
        }
    }
}

Performing more async operations inside the poll_read function will become complex, as you will have to keep track of the multiple states your AsyncRead implementation will be in. async/await is an abstraction over lower level future state machines. If you don't necessarily require implementing AsyncRead, you can avoid lower-level futures entirely and just make this an async method:

pub async fn read(mut input: impl AsyncRead + Unpin) -> Vec<u8> {
    let mut buf = [0u8; 1024];
    input.read(&mut buf).await;
    // ...
    buf.to_vec()
}

If you choose to go the lower-level route, here are some useful resources:

Ibraheem Ahmed
  • 11,652
  • 2
  • 48
  • 54
  • 1
    Thank you for the explanation, helped me understand this. I already tried the `poll_read` before, however for some reason it didn't work reading multiple times. Probably bug in my code. Will try again if my stream workaround fails. – Makalone LOgman Apr 24 '21 at 17:17
  • @MakaloneLOgman Futures can be hard to write manually and are easy to get the wrong as the flow can be unintutive. Also note that if you return `Poll::Pending` the executor will never poll you again unless you [tell it to wake you up](https://doc.rust-lang.org/std/task/struct.Waker.html). I'll link some resources in the answer to help you get started with lower-level async if you choose to go that route. – Ibraheem Ahmed Apr 24 '21 at 17:21
  • Yes, am trying right now, anything will help. – Makalone LOgman Apr 24 '21 at 17:29
  • It's interesting that 1 out of only 5 unique words in the useful resources is literally 'suffering' though the other two are 'Async' and 'Await' – oliver Apr 24 '21 at 18:45
  • @oliver Well, low level async Rust is hard, there is no question about that. It's just that `async`/`await` is still very new and many lower-level details are still being exposed to beginners. – Ibraheem Ahmed Apr 24 '21 at 18:52
  • I'm scared to open it :/ – oliver Apr 24 '21 at 19:15