Reputation: 555
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
Reputation: 154846
there any more data types besides
Option
which returnsMutexGuard
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
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