A subtle difference when `mem::replace`

use std::mem::replace;

fn main() {    
    let mut v = 0;

    // let old_v = replace(&mut v, v + 1); // error[E0503]: cannot use `v` because it was mutably borrowed

    let p = &mut v;
    let old_v = replace(p, *p + 1); // this works fine

    assert_eq!(0, old_v);
    assert_eq!(1, v);
}

I can understand why replace(&mut v, v + 1) causes the error, but I don't understand why replace(p, *p + 1) passes.
I found the subtle difference by accident.
Is there any explanation about this? Thanks in advance.

fn main() {
    let mut v = 0;
    {
        let p = &mut v;
        f(p, *p); // passes
        // f(p, v);  // fails
    }
    // f(&mut v, v); // fails
    // f(&mut v, v.clone()); // fails
}

fn f(_: &mut i32, _: i32) {}

Well, the Copy trait when dereferencing seems prior.
Maybe this is the point here.

It's a "two-phase borrow".

p is first reborrowed as &i32, then *p + 1 is calculated, and finally the &i32 reference gets upgraded to &mut i32.

2 Likes

Well, the arguments get prepped from left to right, and then the function gets called. So in the first version it's something like...

let arg0 = &mut v;
let arg1 = v + 1;
let old_v = replace(arg0, arg1);

And that gives the same error -- using v in v+1 invalidates the mutable borrow. Where as the new version is

let arg0 = &mut v;
let arg1 = *arg0 + 1;
let old_v = replace(arg0, arg1);

And there is no direct use of v to invalidate the borrow (arg0). You're utilizing the borrow for a short period to do the addition, and then again when you call the function.

I think it's just NLL.

1 Like

This wasn't quite expanded directly, it's more like this:

let arg0 = p;
let arg1 = *p + 1;
let old_v = replace(arg0, arg1);

which does not compile.

3 Likes

Good point. Passing the &mut as an argument doesn't move it, it reborrows it, but that doesn't solve the problem. So it's not just NLL.

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.