Unable to wrap an AsyncBufRead inner object due to returning data owned by current function

227 views Asked by At

I am trying to wrap a stream object with the goal of introducing some timeouts on it's read operations, and am starting with just the bare skeleton, but not even this compiles:

use futures::io::{AsyncBufRead, AsyncRead};
use std::io;
use std::io::Result;
use std::pin::Pin;
use std::task::{Context, Poll};

pub struct ImpatientStream<T> {
    inner: Pin<Box<T>>,
}

impl<T> ImpatientStream<T> {
    pub fn new(stream: T) -> Self {
        Self {
            inner: Box::pin(stream),
        }
    }
}

impl<T: AsyncRead + Unpin> AsyncRead for ImpatientStream<T> {
    fn poll_read(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        buf: &mut [u8],
    ) -> Poll<Result<usize>> {
        self.inner.as_mut().poll_read(cx, buf)
    }
}

impl<T: AsyncBufRead + Unpin> AsyncBufRead for ImpatientStream<T> {
    fn poll_fill_buf(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
        self.inner.as_mut().poll_fill_buf(cx)
    }

    fn consume(mut self: Pin<&mut Self>, amt: usize) {
        self.inner.as_mut().consume(amt)
    }
}

fn main() -> io::Result<()> {
    Ok(())
}

When I try to compile this I get:

error[E0515]: cannot return value referencing function parameter `self`
  --> src/bin/echo-server-copy.rs:31:9
   |
31 |         self.inner.as_mut().poll_fill_buf(cx)
   |         ----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         returns a value referencing data owned by the current function
   |         `self` is borrowed here

Am I doing something wrong or is this just not possible to be done like this?

1

There are 1 answers

0
br0kend On BEST ANSWER

For anyone coming across this in the future, the trick lies in what self actually is. It's easy to miss that self is actually a Pin rather than a reference to self.

As far as I understand it, the expression self.inner ends up returning a Pin which itself has a reference to self - hence the compiler message that we return a value referencing data owned by the current function.

The workaround I found here is to use Pin::get_mut which allows us to get a &mut self, and thus return a reference to its field self.inner.

Updating the above example on line 31:

        Pin::get_mut(self).inner.as_mut().poll_fill_buf(cx)

This can be confirmed on the playground

Also this discussion was helpful in understanding what's happening here.