Tokio AsyncRead + AsyncWrite cannot borrow as mutable in Pin

Hi all,

I'm quite new to Rust and ownership and borrowing has been one of the hardest things to get my head around. I'm writing a proxy using tokio for async and I'm trying to use the AsyncRead and AsyncWrite traits so that I can use the same function for both a TcpStream and an SslStream.

I've been working on this on and off for several months so I can't remember exactly how I got to this point, but the below code is what I currently have, and the compiler complains that s cannot be borrowed as mutable (this line: let s = Pin::<&mut T>::new(&mut s);).

I'm guessing that this is either something to do with asynchronicity or with Pin, but if I remove the mut from all of the relevant declarations, it then complains that poll_read doesn't exist in s.

async fn handle_http_request<T: AsyncRead + AsyncWrite + Unpin>(mut socket: &T) -> Result<(), Error> {
    let mut request_string = String::new();
    let mut s = socket.clone();
    poll_fn(|cx| {
        let mut buf = [0; 1024];
        let mut buf = ReadBuf::new(&mut buf);
        loop {
            let s = Pin::<&mut T>::new(&mut s);
            s.poll_read(cx, &mut buf);
            if buf.filled().len() > 0 {
                request_string.push_str(std::str::from_utf8(buf.filled()).unwrap());
                buf.clear();
            } else {
                return Poll::Ready(());
            }
        }
    });
    // ... more code here...
}

Any support would be greatly appreciated. If you need any more information, let me know.

Did you mean to make T bound by + Clone? It looks like at the moment

    let mut s = socket.clone();

is merely cloning the shared reference you passed into the function.

Your function takes an immutable reference to the socket, but the methods on the AsyncRead/AsyncWrite traits require mutable access. That's never going to work.

Try changing the type of socket to &mut T. Then you should be able to call Pin::new(socket).poll_read(...) without problems.

1 Like

Unfortunately neither TcpStream nor SslStream implement clone.

Why are you trying to clone it in the first place?

Thank you! That worked. I thought that by declaring mut socket: &T in the function signature that it would make it a mutable reference.

Oh. That mut is not part of the type. It would just let you change which socket the reference points at - it doesn't make the reference itself mutable,

1 Like

I'm not sure to be honest. There was probably some logical reason for that at some point but I've now removed it.

I think you might find it easier to use AsyncWriteExt/AsyncReadExt. These are extension traits which add additional methods to types implementing AsyncWrite/AsyncRead, and you wouldn't have to call poll methods manually (and I think doing it in a loop is all wrong, you would have to return the result from poll_read instead).

For example, you should be able to do something like:

use tokio::io::AsyncReadExt;

async fn handle_http_request<T: AsyncRead + AsyncWrite + Unpin>(socket: &mut T) -> Result<(), Error> {
    let mut buf = [0; 1024];
    let n = socket.read(&mut buf[..]).await?;
    // ... call std::str::from_utf8 and push to request_string here ...
}

@rschoon Oh, yeah, you are right. I didn't really read the part of the code where they used the poll_* methods. A loop like that is definitely wrong.

Thanks @rschoon, I've gotten so bogged down in my attempts to sort this out that I got lost in what I was doing. I've replaced the loopy bit with read_all from AsyncReadExt and it seems to be working correctly up to (and slightly beyond) that point. Thank you both for the help with this, it's really appreciated.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.