0

I would like to create a variation of a SOCKS5 proxy using asynchronous IO. I've done this before in Haskell so I thought would be a good learning challenge. I am starting with the very well-documented SOCKS5 example code.

In the example, the Transfer struct requires the reader and writer to be of type TcpStream. In my code these need to be polymorphic, so I started by defining:

pub trait Pollable {
    fn poll_read(&self) -> Async<()>;
    fn poll_write(&self) -> Async<()>;
}

impl Pollable for TcpStream {
    fn poll_read(&self) -> Async<()> {
        tokio_core::net::TcpStream::poll_read(&self)
    }
    fn poll_write(&self) -> Async<()> {
        tokio_core::net::TcpStream::poll_write(&self)
    }
}

pub trait AsyncStream: tokio_io::AsyncRead + tokio_io::AsyncWrite + Pollable {}

impl<T> AsyncStream for T
where
    T: tokio_io::AsyncRead + tokio_io::AsyncWrite + Pollable,
{}

When I switch the type of reader at line 510 from Rc<TcpStream> to Rc<AsyncStream>, the compiler fails with this error on line 598:

error[E0596]: cannot borrow immutable borrowed content as mutable
   --> src/transfer.rs:125:33
    |
125 |                 let n = try_nb!((&*self.reader).read(&mut buffer));
    |                                 ^^^^^^^^^^^^^^^ cannot borrow as mutable

Ignore the file and line number in the error message; I'm in the middle of moving the code around to multiple files but it is otherwise identical to the example.

Following advice I found elsewhere on Stack Overflow, I checked the types. Before the change, the type of &*self.reader is &tokio_core::net::TcpStream; after the change, the type becomes &transfer::AsyncStream + 'static. I don't know why the 'static lifetime comes in when I change from a concrete type to a trait...

Why does this happen? How can I fix it?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tom Kludy
  • 429
  • 3
  • 11
  • 1
    A guess: Maybe `TcpStream` had a method called `read` that took `&self`, but `AsyncStream` (or one of its supertraits) has a method called `read` that takes `&mut self`? – trent Jan 26 '18 at 14:56

1 Answers1

2

tokio_core::net::TcpStream implements Read and AsyncRead for an immutable reference. Your trait does not.

&*self.reader converts the Rc<T> to a &T, which is used as the target of the read method.

To fix it, you need to either implement the appropriate trait for an immutable reference to your trait or use some kind of interior mutability.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks! This does appear to be my problem. However I fear I just don't understand enough in this area yet... what would the trait for an immutable reference look like? – Tom Kludy Jan 26 '18 at 15:48
  • @TomKludy I'm not sure I understand your question. I [linked to an implementation of a trait for a reference](https://docs.rs/tokio-core/0.1.12/tokio_core/net/struct.TcpStream.html#impl-Read-1), so it seems unlikely that you are asking about the syntax needed for such an implementation. Could you clarify your question a bit? – Shepmaster Jan 26 '18 at 15:52
  • I tried following the pattern you linked and might be closer.. But now I get error: `cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements` Thank you for your patient help! Going to chase this one for a while... – Tom Kludy Jan 26 '18 at 16:06