A puzzle about mutable borrows

So, I have this silly code:

fn main() {
    let mut v: u32 = 4;

    let m = &mut v;
    *m += 1;

    let mm = &mut v;
    *mm += 1;

    println!("{} {}\n", m, mm);
}

of course the compiler complains about that:

error[E0499]: cannot borrow `v` as mutable more than once at a time
  --> src/main.rs:11:14
   |
8  |     let m = &mut v;
   |             ------ first mutable borrow occurs here
...
11 |     let mm = &mut v;
   |              ^^^^^^ second mutable borrow occurs here
...
18 |     println!("{} {}\n", m, mm);
   |                         - first borrow later used here

and I can see why there could be a data race here.

But I'm puzzled why this is instead compiling fine:

fn ff(v: &mut u32) -> u32 {
    *v += 1;
    *v
}
fn main() {
    let mut v: u32 = 4;

    let m = ff(&mut v);
    let mm = ff(&mut v);

    println!("{} {}\n", m, mm);
}

arent't the two snippets basically the same? The only difference I can see is that in the second one I'm now borrowing v using ff.

I mean, in terms of data race isn't the second one still problematic?

Can anyone shed some light on this? :slight_smile:

In the second snippet, ff is returning an owned value (u32) instead of a reference (&mut u32). This lets the borrow you take to make the call end at the end of the statement, which means that there's no conflict anymore.


Consider this version:

fn gg(v: &mut u32) -> &mut u32 {
    *v += 1;
    v
}
fn main() {
    let mut v: u32 = 4;

    let m = gg(&mut v);
    let mm = gg(&mut v);

    println!("{} {}\n", m, mm);
}

Now, m and mm are exclusive references (like in your first version) and the compiler complains about conflicting accesses to v as you'd expect:

error[E0499]: cannot borrow `v` as mutable more than once at a time
  --> src/main.rs:9:17
   |
8  |     let m = gg(&mut v);
   |                ------ first mutable borrow occurs here
9  |     let mm = gg(&mut v);
   |                 ^^^^^^ second mutable borrow occurs here
10 |
11 |     println!("{} {}\n", m, mm);
   |                         - first borrow later used here
2 Likes

Another angle to look at it… let’s make the read of the value of m to be printed explicit. Your first code

fn main() {
    let mut v: u32 = 4;

    let m = &mut v;
    *m += 1;

    let mm = &mut v;
    *mm += 1;

    println!("{} {}\n", m, mm);
}

Is essentially doing as much as

fn main() {
    let mut v: u32 = 4;

    let m = &mut v;
    *m += 1;

    let mm = &mut v;
    *mm += 1;

    let m_val: u32 = *m; // <- !!
    let mm_val: u32 = *mm;
    println!("{} {}\n", m_val, mm_val);
}

The other version, where the functions do the reading instead, is doing as much as

fn main() {
    let mut v: u32 = 4;

    let m = &mut v;
    *m += 1;
    let m_val: u32 = *m; // <- !!

    let mm = &mut v;
    *mm += 1;
    let mm_val: u32 = *mm;

    println!("{} {}\n", m_val, mm_val);
}

If you test these two versions, you’ll see that one compiles and the other doesn’t, accordingly.

It’s the placement of the read of m_val that’s important. If that’s read before mm is created, it will read v’s state (5) at that point and end the access to m so the compiler can infer that there’s no coexisting aliasing mutable references. It it’s at the end it would need to be reading the value (6) after the second modification, but that’s some interleaved aliasing access, which mutable references in Rust are prohibited to do.

2 Likes