Edit: TL;DR: Why is it OK for atomic variables that have not be declared with
let mut to still be mutable?
After following some discussion on Internals, I came to the conclusion that either I'm misunderstanding something with regards to mutability in bindings and references, or there is some arguable (slight) inconsistency in the language.
The Rust book says:
In Rust, the compiler guarantees that when you state that a value won’t change, it really won’t change.
Hence one needs to declare a variable with
let mut in order to modify it, or pass it as a mut reference. But atomic types (seem to) break this promise:
let v = 8u8; // This is immutable let a = AtomicU8::new(8u8); // This isn't foo(&mut v); // error, because immutable a.store(9u8, Ordering::Relaxed); // but this is fine and dandy?!?
In fact, if you declare the atomic variable with
let mut you even get a
warning: variable does not need to be mutable , which furthers the confusion (at least mine).
(Disclaimer: I do know that
&self, which is why it compiles.)
The Advanced View of Things
The explanation for this is that it's not really about mutability, but about shared vs. unique (quote below by @HadrienG, https://docs.rs/dtolnay/0.0.8/dtolnay/macro._02__reference_types.html):
However, that diction then doesn't explain why one needs
let mut to assign or modify variable values, even if there is no sharing involved. IOW, if we rewrite it accordingly:
let v = 8u8; // no `let mut` v = 9u8; // error, even though no sharing (but still understandable) foo(&unique v); // error, even though no sharing (why?)
which moves the confusion to the previous "simple mutability explanation".
IMHO, to be really consistent, we'd need 3 reference kinds:
- immutable (current
- mutable unique (current
- mutable shared (what atomics do)
To illustrate, using
&mut_shr for the new third kind of reference.
let iv = 8u32 let ia = AtomicU32::new(8u32); read(&iv); // fine modify(&mut iv); // ERROR: not mutable // AtomicU32::store now takes `&mut_shr self` ia.store(3u32, Ordering::Relaxed); // ERROR: not mutable (NEW!) let mut mv = 8u32; let mut ma = Atomic::new(8u32); // `let mut` now required for modifications read(&mv); // still fine modify(&mut mv); // fine ma.store(3u32, Ordering::Relaxed); // now fine as well
Of course, the benefits of adding such a third reference type likely don't come close to the complications it'd created. OTOH, this should really only be required for very few library types, and can likely be restricted to the
I'm not really advocating we should make this change - I just would like to know whether this all makes sense, or am I really really misunderstanding something?