Take this example:
fn main() {
let dato = std::sync::Arc::new(std::sync::Mutex::new(1u8));
for _ in 0..3 {
let value = dato.clone();
std::thread::spawn(move || {
let v = value.lock().unwrap();
*v += 1; // <- Error
});
}
std::thread::sleep(std::time::Duration::from_secs(1u64));
println!("{:?}", dato);
}
cannot borrow immutable local variable
vas mutable
I know that changing to mut works:
std::thread::spawn(move || {
let mut v = value.lock().unwrap();
*v += 1;
});
but why does this work:
let value = dato.clone();
std::thread::spawn(move || {
*value.lock().unwrap() += 1;
});
playground
value.lock().unwrap() returns a value of type MutexGuard, which has a DerefMut implementation:
impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
fn deref_mut(&mut self) -> &mut T { ... }
}
DerefMut::deref_mut(x) is equivalent to &mut *x; naturally, DerefMut is also used for assignments under the pointer, like in your case.
Therefore, for *v += 1 to work, v should be a mut variable - otherwise it would be impossible for DerefMut::deref_mut to be invoked at all.
*value.lock().unwrap() += 1 works because now value.lock().unwrap() is a temporary variable without an explicit binding, so Rust is free to assign its mutability automatically.
The fact that Mutex contains an UnsafeCell inside is not related to this particular thing about DerefMut directly; however, it does mean that Mutex provides something called internal mutability, i.e. it allows one to mutate its contents through a shared reference. You can read more on it in the book.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With