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
v
as 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