Valian Masdani
Valian Masdani

Reputation: 555

Rust accessing Option from mutex

I am having trouble understanding how to modify Option inside a Mutex.

When there's no Option, it works fine

let mut my_int = Arc::new(Mutex::new(5));

let my_int_clone = Arc::clone(&my_int);

thread::spawn(move || {
  let mut my_int = my_int_clone.lock().unwrap();
  *my_int += 1;
}).join();

let my_int_clone_print = Arc::clone(&my_int);
println!("Value: {}", my_int_clone_print.lock().unwrap());

However, when I wrap the value in Some, I had to manually use ref mut and such (I found it from here) because the lock().unwrap() returns MutexGuard, not the Option itself.

let mut my_int = Arc::new(Mutex::new(Some(5)));

let my_int_clone = Arc::clone(&my_int);

thread::spawn(move || {
  let mut my_int = my_int_clone.lock().unwrap();

  match *my_int {
    Some(ref mut val) => {
      *val += 1;
    },
    None => {
      println!("Value is None. Doing nothing..");
    }
  }

}).join();

let my_int_clone_print = Arc::clone(&my_int);
println!("Value: {}", my_int_clone_print.lock().unwrap());

Any idea which Rust concept causes this? And also are there any more data types besides Option which returns MutexGuard and not its original value?

Upvotes: 5

Views: 13281

Answers (2)

user4815162342
user4815162342

Reputation: 154846

there any more data types besides Option which returns MutexGuard and not its original value?

MutexGuard can't return the original value because moving the value would invalidate the mutex. Instead, it's a wrapper that provides a mutable reference to the original value.

That's in no way specific to Option, a MutexGuard is what Mutex::lock always returns. For example, this code:

let m = Mutex::<bool>::new(false);
let () = m.lock().unwrap();

...will report that the type returned by m.lock().unwrap() is std::sync::MutexGuard<'_, bool>.

MutexGuard gives out access to the data under the condition that the reference does not outlive the guard. *my_int += 1 works because MutexGuard implements DerefMut, which tells the * operator what reference to work on. And the * operator works perfectly fine with Option; for example:

let m = Mutex::<Option<i32>>::new(Some(0));
let mut my_int = m.lock().unwrap();
*my_int = Some(100);

Matching an *my_int can be done without ref mut, but then *my_int will copy the option (which works as long as its contents are Copy) and modifying the value will have no effect on the option itself. This is again in no way specific to MutexGuard, it's how matching works. The ref mut is required to give you mutable access to the data inside the option.

Upvotes: 5

Kitsu
Kitsu

Reputation: 3435

Actually, Mutex::lock returns Result<MutexGuard, ..> in both cases. Though, this type has interesting trait implementation: Deref and DerefMut. These allow explicit dereference via * operator. Consider this example with the explicit types:

let mutex = Mutex::new(1i32);
let mut guard: MutexGuard<'_, i32> = mutex.lock().unwrap();

// This dereferences to &mut i32 
// because assignment operator works with &mut self.
*guard = 2;

// Nevertheless, for an explicit borrowing you need &mut
// because otherwise it would be moved from the guard.
let inner: &mut i32 = &mut *guard;

And, of course, you can use Option similarly:

let mutex = Mutex::new(Some(1i32));
let mut guard: MutexGuard<'_, Option<i32>> = mutex.lock().unwrap();

// Directly change inner value
*guard = Some(2);

// or use in match, notice &mut borrowing
match &mut *guard {
    Some(x) => *x += 1,
    None => {},
}

Notice, the last match example is exactly the same as yours but uses a slightly different syntax. Playground.

Upvotes: 20

Related Questions