Why can I just pass an immutable reference to BufReader, instead of a mutable reference?

Viewed 1452

I am writing a simple TCP-based echo server. When I tried to use BufReader and BufWriter to read from and write to a TcpStream, I found that passing a TcpStream to BufReader::new() by value moves its ownership so that I couldn't pass it to a BufWriter. Then, I found an answer in this thread that solves the problem:

fn handle_client(stream: TcpStream) {
    let mut reader = BufReader::new(&stream);
    let mut writer = BufWriter::new(&stream);

    // Receive a message
    let mut message = String::new();
    reader.read_line(&mut message).unwrap();

    // ingored
}

This is simple and it works. However, I can not quite understand why this code works. Why can I just pass an immutable reference to BufReader::new(), instead of a mutable reference ?

The whole program can be found here.

More Details

In the above code, I used reader.read_line(&mut message). So I opened the source code of BufRead in Rust standard library and saw this:

fn read_line(&mut self, buf: &mut String) -> Result<usize> {
    // ignored
    append_to_string(buf, |b| read_until(self, b'\n', b))
}

Here we can see that it passes the self (which may be a &mut BufReader in my case) to read_until(). Next, I found the following code in the same file:

fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>)
                                   -> Result<usize> {
    let mut read = 0;
    loop {
        let (done, used) = {
            let available = match r.fill_buf() {
                Ok(n) => n,
                Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
                Err(e) => return Err(e)
            };
            match memchr::memchr(delim, available) {
                Some(i) => {
                    buf.extend_from_slice(&available[..i + 1]);
                    (true, i + 1)
                }
                None => {
                    buf.extend_from_slice(available);
                    (false, available.len())
                }
            }
        };
        r.consume(used);
        read += used;
        if done || used == 0 {
            return Ok(read);
        }
    }
}

In this part, there are two places using the BufReader: r.fill_buf() and r.consume(used). I thought r.fill_buf() is what I want to see. Therefore, I went to the code of BufReader in Rust standard library and found this:

fn fill_buf(&mut self) -> io::Result<&[u8]> {
    // ignored
    if self.pos == self.cap {
        self.cap = try!(self.inner.read(&mut self.buf));
        self.pos = 0;
    }
    Ok(&self.buf[self.pos..self.cap])
}

It seems like it uses self.inner.read(&mut self.buf) to read the data from self.inner. Then, we take a look at the structure of BufReader and the BufReader::new():

pub struct BufReader<R> {
    inner: R,
    buf: Vec<u8>,
    pos: usize,
    cap: usize,
}

// ignored
impl<R: Read> BufReader<R> {
    // ignored
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn new(inner: R) -> BufReader<R> {
        BufReader::with_capacity(DEFAULT_BUF_SIZE, inner)
    }

    // ignored
    #[stable(feature = "rust1", since = "1.0.0")]
    pub fn with_capacity(cap: usize, inner: R) -> BufReader<R> {
        BufReader {
            inner: inner,
            buf: vec![0; cap],
            pos: 0,
            cap: 0,
        }
    }

    // ignored
}

From the above code, we can know that inner is a type which implements Read. In my case, the inner may be a &TcpStream.

I knew the signature of Read.read() is:

fn read(&mut self, buf: &mut [u8]) -> Result<usize>

It requires a mutable reference here, but I only lent it an immutable reference. Is this supposed to be a problem when the program reaches self.inner.read() in fill_buf() ?

1 Answers
Related