Wrapping AsyncRead `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement

Viewed 146

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.

1 Answers

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:

Related