Hello.
Even though I'm new to Rust, I absolutely loved it for its safety, speed, meta-programming and other great features that other programming languages doesn't have.
However, there are certain problems in Rust I haven't had with other languages, particularly with the borrow checker.
I've always been trying to write a clean, readable code. Following the recommendations of Robert C. Martin from his book Clean Code, I always strive to decompose functions into smaller functions, into logically connected blocks of code. In other programming languages I had no problem with that, unlike in Rust.
Here is a simplified version of my code:
struct SomeType {
list: Vec<u32>,
integer: u32,
}
impl SomeType {
fn new(list: Vec<u32>) -> Self {
Self {
list: list,
integer: 0,
}
}
fn update(&mut self) {
for w in self.list.iter() {
// SOME CODE
// A LOT OF CODE ...
// A LOT OF CODE ...
// A LOT OF CODE ...
self.integer += w;
// A LOT OF CODE ...
// A LOT OF CODE ...
// A LOT OF CODE ...
// SOME CODE
}
}
}
fn main() {
let mut t = SomeType::new(vec![1, 2, 3, 4]);
t.update();
}
I intended to extract a method subfunction
from the update
function to make the code a little bit cleaner.
So now the code looks like this:
fn update(&mut self) {
for w in self.list.iter() {
// SOME CODE
self.subfunction(*w);
// SOME CODE
}
}
fn subfunction(&mut self, w: u32) {
// A LOT OF CODE ...
// A LOT OF CODE ...
// A LOT OF CODE ...
self.integer += w;
// A LOT OF CODE ...
// A LOT OF CODE ...
// A LOT OF CODE ...
}
But after this seemed to be usual operation, the code doesn't compile anymore, for the following reason:
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/bin/test1.rs:16:13
|
15 | for w in self.list.iter() {
| ----------------
| |
| immutable borrow occurs here
| immutable borrow later used here
16 | self.subfunction(*w);
| ^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
Probably, I understand why it happens. The borrow checker doesn't allow me to call the subfunction
function, because otherwise the subfunction
could potentially modify the list
field, while it is being iterated in the update
function, potentially breaking an invariant in the Vec type.
While it is an absolutely correct restriction by the borrow checker to prevent any potential errors, sometimes it may be annoying that even a function decomposition can break the code, while semantically the code does the same thing.
How do you deal with such restrictions by the borrow checker, particularly when decomposing functions?
What do you think would be a better way to decompose the update
function in my example preserving all the safety rules without compiler errors, and preferably without any runtime bloat like RefCell
?
Thank you!