I apologize for the long-winded question, but this is an issues that has come up often on the forum and I am curious if my workaround is sound or if I am doing something I should not be.
Sometimes you have a complicated structure with multiple methods that take a reference to self
. Often you will have some public outer method that calls one or more private inner methods to mutate self
, often in the context of a loop. For example:
impl MyStruct {
pub fn outer_method(&mut self) {
for thing in &self.things {
self.inner(thing);
}
}
fn inner_method(&mut self, thing: Thing) {
// ...
}
}
error[E0502]: cannot borrow
*self
as mutable because it is also borrowed as immutable
Please see the Rust Playground for a complete, minimal example.
The Rust borrow checker understands that we can have multiple borrows to disjoint fields in a structure, so we could have safely mutated self
directly inside the for loop, for example:
pub fn outer_method(&mut self) {
for thing in &self.things {
self.bar = thing + self.foo;
}
}
The problem arises when we call self.inner_method()
, which borrows self mutably. There is no way for the borrow checker to know whether self.inner_method()
is going to try and borrow self.things
again or not. But if our inner method does something long and complicated, then we would really like to split that code out into a separate method to make the outer method more readable!
RFC 1215 proposes a partial borrow syntax that could solve this, but insofar as it's been around since 2015 I'm not holding by breath.
There are multiple other forum posts asking about this problem:
- Solved by cloning a
String
to avoid the borrow altogether - Kind-of solved by making references
'static
. - Solved by using a traditional indexed loop instead of an iterator. But I consider this an anti-pattern.
- Several more posts elsewhere that involved destructuring
self
, refactoring associated functions/methods to be free functions, and other un-ergonomic strategies that significantly reduced code readability.
I am wondering what the community things about using Rc
as a general-purpose workaround in these situations.
struct MyStruct {
things: Rc<Vec<Thing>>,
// ...
}
impl MyStruct {
pub fn outer_method(&mut self) {
let things = self.things.clone();
for thing in self.things.iter() {
self.inner(thing);
}
}
fn inner_method(&mut self, thing: Thing) {
// ...
}
}
See the complete example on Rust Playground. To me this seems ergonomic, requires little modification to the structure or method design, and has very little performance penalty as the Rc
is reasonably lightweight and only the Rc
(not the entire vector) is cloned.
Can the community think of any pitfalls I am not seeing or suggest a more idiomatic alternative?