Borrow check issues with `MutexGuard`

I'm having trouble getting my code to compile: MutexGuard is enforcing borrow checking rules that seem to be unnecessary to me. In particular, declaring the same struct without wrapping it in the Mutex would allow both mutable references to the disjoint fields of the struct.

use std::sync::Mutex;

struct S {
    a: usize,
    b: usize,
}

fn main() {
    let s = Mutex::new(S { a: 0, b: 1 });
    let mut s = s.lock().ok().expect("mutex error");

    let a = &mut s.a;
    let b = &mut s.b;

    *a = 1;
    *b = 2;
}

Compiler error:

error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:15:18
   |
14 |     let a = &mut s.a;
   |                  - first mutable borrow occurs here
15 |     let b = &mut s.b;
   |                  ^ second mutable borrow occurs here
16 | 
17 |     *a = 1;
   |     ------ first borrow later used here

What can I do to bypass the above issue?

Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=221ccab56214aeff21482776dda96394

Dereferencing mutex guard dereferences the whole thing (dereferences the guard, then the field). Try:

    let mut s = s.lock().expect("mutex error");
    let mut s = &mut *s;

which dereferences the guard once, and the works on the "raw" object, where rust can track individual fields.

2 Likes

It seems that the root of this issue lies in the way we get access to the value behind the MudexGuard, i.e. the Deref trait, which is used implicitly. deref_mut has the signature (&mut self) -> &mut T, which is, due to elision rules, is equivalent to (&'a mut self) -> &'a mut T. I.e., the whole MutexGuard is borrowed for the lifetime of the output borrow.

When you use the bare struct, however, you directly borrow its fields, without any implicit methods being called and so without any implicit lifetimes appearing, and the compiler is smart enough to see that these borrows are disjoint.

2 Likes