If you have a struct Foo { a: String, b: String } where a and b are not pub but rather have one getter/accessor method each (get_mut_a(&mut self) -> &mut String, likewise for b), you cannot borrow both at the same time.
The following is barely a pre-pre-RFC, and also a disclaimer ahead: I couldn’t find any discussions on here or on Reddit although I am sure they exist because this issue, together with e.g. self-referencing structs, is IMO one of the most annoying limitations of the borrowchecker (I promise I searched thoroughly, but I also didn’t know under which name this issue is known)
To me the problem here is that both get_mut_a and get_mut_b mutably borrow self in its entirety. Explicitly, in their signatures. What if they didn’t have to?
Currently get_mut_a may implemented like this:
impl Foo {
fn get_mut_a(&mut self) -> &mut String { &mut self.a }
}
But what if it was:
impl Foo {
fn get_mut_a(&mut self.a) -> &mut String { &mut self.a }
}
-
self.a is still not a pub field, but it is exposed in the method signature. The API consumer knows that their Foo is not completely borrowed (and can rely on the fact that get_mut_b will not borrow the same part of Foo as get_mut_a), and can see that there is a field called a inside the struct. But they can’t access it directly as long as it isn’t marked as pub explicitly.
- Inside
get_mut_a, accessing self.b just doesn’t work. It’s not “in scope”.
- if you need multiple fields, I guess you could write
&mut self.{a,b}? Seems roughly consistent with how use works at least. EDIT: perhaps adding two self parameters is nicer: fn foo(&mut self.a, &mut self.b). Then you can also have distinct lifetime parameters, borrow one field as immutable etc
Not everything is a struct field
This solution is highly specific to struct fields. This is also a problem, but not at all solved by the new syntax above:
let mut foo = vec![1,2,3];
let a = foo.get_mut(1);
let b = foo.get_mut(2);
My dream is that, at some point, the direct correlation between &mut self.a in a function signature and an actual struct field a is removed. Such that one can introduce arbitrary “phantom fields” to tell the borrow checker which method calls on a struct would borrow which parts of the struct. Something like:
impl Vec<T> {
fn get_mut(&mut self[i], i: usize) -> Option<T> {
// unsafe block here???
}
}
What do you think?