Why &mut T has different behavior?

I know &mut T is not Copy, but when passed to function , it seems it behave like Copy. I guess there is some special rule for this inconsistent.

fn mv() {
  let mut a = 0;
  let x = &mut a;
  let y = x;   // x moved
  x;           // can't compile as expected
}
fn mv2() {
  let mut a = 0;
  let x = &mut a;
  fn test(x:&mut u8) {}
  test(x);  // seems copied
  x;        // can compile , unexpected
}

This behaviour is called "reborrowing".

When you write test(x) and x is an &mut T, the compiler will automatically rewrite it as test(&mut *x) so the lifetime of the &mut T reference passed into the function is shortened to last until the function call returns.

6 Likes

thank you. I'll check it.

Note that this works too

fn mv() {
  let mut a = 0;
  let x = &mut a;
  let y: &mut _ = x;   // x moved
  x;           // can't compile as expected
}
2 Likes

You can see that this is reborrowing and not moving if you use generics:

fn main() {
    trait Trait { }
    impl Trait for &'_ mut i32 {}
    fn generic(_: impl Trait) {}
    let mut a = 0;
    let x = &mut a;
    generic(x);
    x; // Doesn't compile, x moved
}

Another, more complicated way is to inspect the MIR (simplified here):

fn mv() -> () {
    let mut _1: i32; // a
    let _2: &mut i32; // x
    let mut _4: &mut i32; // The reborrowed argument

    bb0: {
        _1 = const 0_i32; // a = 0;
        _2 = &mut _1; // x = &mut a;
        _4 = &mut (*_2); // <argument> = &mut *x;
        test(move _4) -> bb1; // test(<argument>);
    }
}

Like you see, we first reborrow x and then move this borrow.

1 Like

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.