1

My intention is to access to a TcpStream and perform two read operations in two different methods of a struct that holds the TcpStream as attribute.

The first operation performs fine but when I try to load the remaining bytes on the second method, the buffer cannot be filled.

I have tried to create a very simple recreation. This is how it works:

  1. Some data is sent to a socket (8 bytes)
  2. 1 byte is read using read and a buffer of 1 byte size. Everything is fine.
  3. 1 byte is read using read_exact and a buffer of 1 byte size. Everything is fine.
  4. 1 byte should be read using read_exact over the underlying read object (stream object). The buffer cannot be filled. I get an error if I unwrap, or the buffer with the initial values.
#[cfg(test)]
mod tests {
    use std::net::{TcpListener, TcpStream};
    use std::io::{BufReader, Read, Write};

    #[test]
    fn test_read_twice() {
        let listener = TcpListener::bind("127.0.0.1:0").unwrap();
        let local_addr = listener.local_addr().unwrap();
        let mut stream = TcpStream::connect(local_addr).unwrap();
        match listener.accept() {
            Ok((mut socket, _)) => {
                let _ = socket.write_all(&[0, 1, 2, 4, 5, 7]);
            }
            Err(e) => println!("couldn't get client: {:?}", e),
        }
        {
            let mut reader = BufReader::new(&mut stream);
            let mut buff = vec![0u8; 1];
            let _ = reader.read(&mut buff[..]).unwrap();
            assert_eq!(buff, vec![0]);
            let mut buff = vec![0u8; 1];
            let _ = reader.read_exact(&mut buff[..]).unwrap();
            assert_eq!(buff, vec![1]);
        }
        let mut buff = vec![88u8; 1];
        let _ = stream.read_exact(&mut buff[..]);
        assert_eq!(buff, vec![2]);
    }
}

gist

1 Answer 1

4

The point of a BufReader is to read "ahead" - you only did small reads, but the BufReader read a large block from the underlying reader, stored it in a buffer, and serves the read requests from that. This buffer is owned by the BufReader - you can't expect to read from that buffer when you call read (or read_exact) on the underlying reader.

As the BufReader interface doesn't expose the internal buffer it usually doesn't make sense to use the underlying reader again later (you can't reliably get to a state where the buffer is empty). So instead of passing a reference to BufReader::new simply pass it by value. That way you can leave it there forever and still move the BufReader/TcpStream around if necessary.

You still can access the underlying reader through get_ref and get_mut if you need to call other (not Read-related) functions (or even get it back with into_inner, but you'll lose buffered data).

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the answer! This means that in this case, BufReader read everything, and thus, there's nothing left to be read, right? I need the feature read_line and also to be able to read afterwards from the original stream. Any idea?
@Blas don't unwrap it. Just leave it in the BufReader forever.
@Shepmaster could you give me an example where a BufReader is wrapped in a struct? In that case, I could use it across several methods. I don't mind creating a new question, if that would be the proper way here.
@Blas not sure what you mean with "wrapped in a struct", but maybe my updated answer contains what you're looking for.
That was the best answer. I never realized that I could pass it by value. Thanks a lot, @Stefan

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.