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