Let's say we have declared an immutable (not let mut) RwLock instance like:
let value = RwLock::new(0);
Because the value is immutable, I expected that I can not change the inner value of the RwLock. However when I tested, apparently this works:
{
*value.write().unwrap() = 5;
}
I am wondering if I used RwLock in a wrong way, that this should not have happened (I am worried that the lock might not actually function as expected, if this is the case). However, I am sure there's some explanation behind this behavior since Rust is very explicit when it comes to what you can change and what not.
My guess is the RwLock stores its inner value on the heap so it only needs to keep track of an immutable pointer to the value. So, whenever we write to the value the RwLock struct itself would be kept untouched since the pointer wouldn't change.
This is just a guess, and probably a wrong one. I am more than happy if someone would like to correct me.
For clarification: I know how Reader-Writer locks are supposed to work. My question is not about the synchronization mechanism, but rather why doesn't Rust treat RwLock values like any other value when it comes to immutability. Like is it one of the "magic" types that compiler treats differently, or there's something else I am not aware of.
Because you are not mutating the
RwLock, as far as the compiler is concerned.The
RwLock::write()takes a&selfas the receiver and returns aRwLockWriteGuardfor which theDerefMut-impl is used to do the assignment*value... = 5. What happens in between those steps is up to the implementation ofRwLock.Since you are specifically asking not about
RwLockper se, but the presence or non-presence of "magic": There is absolutely no magic involved here with respect to immutable (&) or mutable (&mut) references. In fact, there may be a misunderstanding:While we call these "immutable" or "mutable" (because that's how they are used), those are better thought of as "shared" and "exclusive" references. With an exclusive reference (
&mut), you are always free to mutate the value without causing mutation-while-aliasing; the compiler can guarantee this (special) use case. Likewise, with shared references (&), you are free to alias the value as mutation is not possible. The compiler can also guarantee this (special) case.While this covers a surprisingly large number of situations, with
RwLockthis is neither possible nor desirable. TheRwLockuses it's internal locking mechanism (that the compiler can't know about) to determine if mutation should be allowed, and upholds the guarantee that mutation-while-aliasing does not occur. That is whyRwLockcan go from a&selfto mutating the inner value.The same is true for the
RefCell-type, btw.