Simple Program Rejected with E0506?

Hi,

The following program is currently rejected by rustc:

fn main() {
  let mut x = Box::new(0); let mut y = Box::new(&mut x); y = Box::new(*y);
}

This programs seems to me like it should be fine, since the borrow &mut x is simply moved into the new box. I'm wondering whether there is any specific reason this is not allowed?

1 Like

Here's a version that works:

fn main() {
    let mut x = Box::new(0);
    let mut y = Box::new(&mut x);
    y = Box::new(*{y});
}

I suspect the reason this happens is reborrowing. The above snippet prevents this by explicitly moving from y (via a block).

In effect, I believe the compiler is rewriting your code to

fn main() {
    let mut x = Box::new(0);
    let mut y = Box::new(&mut x);
    y = Box::new(&mut **y);
}

This is what allows you to do things such as e.g. pass a &mut _ variable to a function even though &mut _ is not Copy; the compiler reborrows the mutable borrow and gives the function a new borrow with a shorter lifetime borrowed from yours. (That's a lot of "borrow"...)

(This question may be a better fit on users than internals, as it has nothing to do with development of the language.)

3 Likes

Ok, that does make some sense. Basically, it reborrows when it shouldn't. Yes, I've heard of that happening before! Interesting that *{y} forces a move.

Interesting stuff.. while exploring this kind of behavior I noticed that I can assign a reborrow of a mut reference to itself, which doesn’t make sense to me either. What kind of rule is behind this and could it be extended to make the original Box example work without the block?

fn foo(_: &mut Box<i32>) {}

// me trying lots of stuff:
pub fn main()  {
    let mut x = Box::new(0);

    // --------------------------
    let mut y: Box<&mut Box<i32>> = Box::new(&mut x);
    // y = Box::new(*y); // doesn’t work
    y = Box::new(*{y}); // but this does
    foo(*y); // advantage: *y doesn’t move anything
    foo(*y);

    // --------------------------
    let mut z = &mut x;
    z = z; // compiler figures not to reborrow, so that it’s okay?
    foo(z); // doesn’t move anything, because it IS (implicitly) a reborrow
    foo(z);

    // --------------------------
    let mut y1: &mut &mut Box<i32> = &mut &mut x;
    y1 = &mut *y1; // explicit reborrow; why does this work??
                  // (similarly above, z = &mut *z would’ve worked...)
    foo(*y1);
    foo(*y1);
}