Another rust mutable borrow puzzle

Why does the second slice get truncated, but not the first?

use std::cell::RefCell;

fn main() {
    {
        let mut v = vec![100];
        let refcell: RefCell<&mut [u8]> = RefCell::new(&mut v);
        {
            let mut ref_borrow = refcell.borrow_mut();
            let mut slice: &mut [u8] = *ref_borrow;
            write(&mut slice);
        }
        println!("{:?}", refcell); // RefCell { value: [1] }
    }
    {
        let mut v = vec![100];
        let refcell: RefCell<&mut [u8]> = RefCell::new(&mut v);
        {
            let mut ref_borrow = refcell.borrow_mut();
            write(&mut *ref_borrow);
        }
        println!("{:?}", refcell); // RefCell { value: [] }
    }
}

fn write(w: &mut &mut [u8]) {
    let w_ = std::mem::replace(w, &mut []);
    let b = &mut w_[1..];
    *w = b;
}

/*
The above snippet is a simplification of the following:

impl Write for &mut [u8] {
    #[inline]
    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
        let amt = cmp::min(data.len(), self.len());
        let (a, b) = mem::replace(self, &mut []).split_at_mut(amt);
        a.copy_from_slice(&data[..amt]);
        *self = b;
        Ok(amt)
    }
}
*/

(Playground)

Output:

RefCell { value: [100] }
RefCell { value: [] }

The top example is a reborrow of the slice itself, like so:

let mut slice: &mut [u8] = &mut **ref_borrow;

And you can see the same thing in the bottom example if you reborrow the inner *ref_borrow like so:

write(&mut &mut **ref_borrow);

Whereas in the bottom example, *ref_borrow is a deref place expression of the contents of the RefCell itself, which you then modify in the call to write. The inner *ref_borrow doesn't get reborrowed.


// Example 2
// In this call notionally at least, this:
//    vvvv
write(&mut *ref_borrow);
// outer &mut is reborrowed, but the inner `*ref_borrow: &mut [u8]`
// is not reborrowed, it's the place in the `RefCell`

Whereas

// Example 1
//      vvvvv this slice is a reborow of...
let mut slice: &mut [u8] = *ref_borrow;
//  ...this expression     ^^^^^^^^^^^^
// ==> &mut **ref_borrow

//    vvvv and this temporary is still notionally reborrowed
write(&mut slice);
// (but doesn't really matter)

It's not the same comparison as the other thread:

// These two scenarios
{ let y = &mut *x; foo(y); }
{ foo(&mut *x) }

Because there's an extra layer of &mut here:

// { let         y = &mut *           x;   foo(         y); }
   { let mut slice = &mut * *ref_borrow; write(&mut slice); }
//                                             ^^^^

Or from the other POV, an extra * here:

//              v
// {   foo(&mut *          x); }
   { write(&mut  *ref_borrow); }
3 Likes

There is something quite subtle going on in this example. If you try this:

let mut slice = *ref_borrow;

You get an error about moving out of ref_borrow, because there's no implicit reborrow unless you use type ascription on the left as discussed here.

But if we do this:

let mut slice: &mut &mut [u8] = &mut *ref_borrow;

We can see that the top example now acts like the bottom example, which is to say the inner &mut (i.e. *ref_borrow) is not reborrowed despite the ascription.

And this matches the behavior of the write function with it's &mut &mut [u8] parameter. Which is good, for as you have demonstrated, behavior is quite unintuitive otherwise!


Perhaps the succinct summary is, "implicit reborrows are shallow / on the outmost &mut".

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.