Search code examples
concurrencyrustmutex

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?


Solution

  • 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.