Rustコトハジメ

プログラミング言語Rustと競プロに関する情報をお届けします。

Learn rvalue from Cursor implementation

In this article, I will share a nice finding when I explore the Cursor implementation of Read trait.

The implementation below means that anything which can lead &[u8] (AsRef<[u8]>) can be an instance of Read by wrapping it by Cursor.

impl<T> Read for Cursor<T> where T: AsRef<[u8]> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        let n = Read::read(&mut self.fill_buf()?, buf)?;
        self.pos += n as u64;
        Ok(n)
    }

The implementation is somewhat moderate compared to the direct implementation to &[u8] which overwrites the fat pointer.

www.rustforbeginners.com

In this implementation I find

let n = Read::read(&mut self.fill_buf()?, buf)?;

this line of code is weird because it directly mutably borrow the returned value of fill_buf rather than putting one step in prior that binds the value by let mut; In textbook knowledge, we have learned that "we need let mut to mutably borrow".

The type of fill_buf is

impl<T> BufRead for Cursor<T> where T: AsRef<[u8]> {
    fn fill_buf(&mut self) -> io::Result<&[u8]> {
        let amt = cmp::min(self.pos, self.inner.as_ref().len() as u64);
        Ok(&self.inner.as_ref()[(amt as usize)..])
    }

So our question is why the returned &[u8] is able to mutably borrow without any mutable binding.

The answer is that the returned value is called rvalue (In Rust, it is called value expression) and it is a fat pointer directing to the buffer on the heap and it is temporary; if we doesn't use the return value from fill_buf and it soon disappears.

The code above is conceptually the same as the follow with extra mutable binding which is needless to say compilable.

let mut p = self.fill_buf()?
let n = Read::read(&mut p, buf)?;