Recursive call of Trait with same function names


#1

I want to create a trait that includes only the read() and write_all() methods from the Read and Write traits respectively. So I created my Transport trait as follows:

pub trait Transport {
    /// Read up to buf.len() bytes from the underlying transport
    fn read(&mut self, buf: &mut[u8]) -> Result<usize, IOError>;

    /// Write all buf.len() bytes to the underlying transport
    fn write_all(&mut self, buf: &mut[u8]) -> Result<usize, IOError>;
}

Now I want to implement this trait for TcpStream, but the compiler warns of a recursive call:

pub trait Transport {
    /// Read up to buf.len() bytes from the underlying transport
    fn read(&mut self, buf: &mut[u8]) -> Result<usize, IOError>;

    /// Write all buf.len() bytes to the underlying transport
    fn write_all(&mut self, buf: &mut[u8]) -> Result<usize, IOError>;
}
warning: function cannot return without recurring
 --> src/tcp_transport.rs:7:5
  |
7 |     fn read(&mut self, buf: &mut[u8]) -> Result<usize, IOError> {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recurring
8 |         return TcpStream::read(self, buf);
  |                -------------------------- recursive call site
  |
  = note: #[warn(unconditional_recursion)] on by default
  = help: a `loop` may express intention better if this is on purpose

warning: function cannot return without recurring
  --> src/tcp_transport.rs:11:5
   |
11 |     fn write_all(&mut self, buf: &mut[u8]) -> Result<usize, IOError> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recurring
12 |         return TcpStream::write_all(self, buf);
   |                ------------------------------- recursive call site
   |
   = help: a `loop` may express intention better if this is on purpose

How do I make this work? How do I just call the underlying read() and write_all() methods for TcpStream without having it be recursive?

The only reason I’m creating this trait at all is because I plan on creating another Transport and want my initialize methods to return impl Transport so the read/write_all calls are irrespective of the underlying Transport implementation. Could/should I be leveraging generics here somehow?!?


#2

Hi,
i simplified your example a bit: https://play.rust-lang.org/?gist=d7b03d7643cae51ff51fd74157356653&version=stable&mode=debug&edition=2015
A compiler error gave me the hint to use <TcpStream as Read>::read(), which i think is what you want, because it solves the ambiguity between Read::read() and Transport::read().
regards :slight_smile:

edit: the compiler error was rustc --explain E0034


#3

To call the method of the Read trait, you can write:

<TcpStream as Read>::read(self, buf)

or you can shorten this to:

Read::read(self, buf)

#4

@mmmmib / @mbrubeck - perfect, thanks!!!