I still struggle to grasp why &mut self is required to modify the internal state of an object owned by my struct. I understand why I have to use at least &self, because I don't want to consume self, ending its existance. I also understand why I have to use &mut self if I were modifying fields of my struct, but I don't.
I have the following struct and implementation:
struct Writer {
obj: json::JsonValue
}
impl<'b> Writer {
fn from(obj: json::JsonValue) -> Self {
Self {
obj
}
}
fn put(&mut self, field_name: &str, value: bool) {
self.obj.insert(field_name, value);
// ^^^- not modifying this, but a field inside "obj"
}
fn release_ownership(self) -> json::JsonValue {
self.obj
}
}
Usage:
writer.put(field_name, true);
With &self instead of &mut self I'd get a compiler error:
`^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable`
The Rust book taught me:
If we wanted to change the instance that we’ve called the method on as part of what the method does, we’d use &mut self as the first parameter.
I don't see how I modify the struct here. I clearly modify a field (here obj) in the struct, but not the struct itself.
In the definition of JsonValue::insert() I can see &mut self, which I do not understand either, since it "only" mutates the contents of object through JsonValue::Object::insert().
The Rust book says:
Note that the entire instance must be mutable; Rust doesn’t allow us to mark only certain fields as mutable.
I can understand this, if I want to modify field values, but not, if the fields are pointers to other structs that are modified.
My intuition tells me, that me qualifying with &mut refers not to the direct reference to data, but to the whole path along all pointers or to the final reference. If this is the case: why is that?
However, it seems redundant, since JsonValue::insert() already enforced mutable access to its internal object. So the compiler knows, that the instance of JsonValue was mutably borrowed for the insert and can not be borrowed again, until it was released.
Homework: read the book and did my search.
Everything along the access chain has to be declared a mutable reference to make sure you can't get two mutable references to the same object. Consider this:
So we can never be allowed mutable acces to something behind a shared reference (something that we accessed through a shared reference somewhere along the path) because we could easily create mutable references to the same object that way.
That assumes the compiler can predict at compile time when the function is called and disallow this, but it can't, or that borrow checking is done at runtime, it's not, borrow checking is done statically at compile time. You can get runtime borrow checking with
RefCell,Mutexor aRwLock.