I have a hard time expressing in Rust code my intention to iterate a field and mutate fields which are not the iterator.
I'll use minimal code to express my intention
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/lib.rs:13:13
|
12 | for &item in &self.some_vec {
| --------------
| |
| immutable borrow occurs here
| immutable borrow later used here
13 | self.mut_with_item(item)
| ^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
and I understand why I get that error, I iterate over data owned by self while mutating self. However - I am confident that the way I mutate self doesn't mutate that particular field.
Let's say that cloning is out of the question, how do I best express my confidence to Rust?
This is also an option, if you can afford to not factor out the loop body into a method:
fn mut_per_item(&mut self){
for item in &mut self.some_vec {
self.field = self.field + *item;
}
}
This uses a feature called "splitting borrows" where you can split the borrow of individual struct fields if you access them directly. This doesn't work if you use mutable methods since in fn blah(&mut self), the &mut self borrows the whole struct.
You can use mutable methods of the individual struct fields though. The above example could be written identically as:
fn mut_per_item(&mut self){
for item in self.some_vec.iter_mut() {
self.field = self.field + *item;
}
}
The only cost with sub-structs is that it's more limiting to layout optimizations, because fields can only be rearranged locally. For example, ((u32, u8), u8) has 3 bytes of padding in the inner tuple, then the outer adds 1 data byte and 3 more padding bytes for total alignment. A flat (u32, u8, u8) only has 2 bytes of padding.
I ended up not having to do it at all but thanks for the help, learned some new things.
Also, I'll dare say regarding the sub-struct vs Cell approach:
sub-struct is stricter and will prevent you from mistakenly modifying the iterator but make it harder to change in the future, e.g. you want to modify fields outside of that particular sub-struct, which means moving fields from the outer struct to the inner one.
Cell is more flexible and you will be able to modify all fields wrapped with a Cell as you see fit, but may open a door to unexpected bugs if another person comes in and modifies the iterator unknowingly.
I have a similar case, where I need to have a mutable reference and an inmutable one:
Here, the function find() takes a mutable reference to the first element in a list, and iterates again over the list in order to find duplicates. If it finds a duplicate, the first element should be changed (the example is a bit silly, but you get the idea).
Can the code be written in a different way in order to avoid using Cell/RefCell?
impl Board {
pub fn find(&mut self) {
let (first_element, tail) = self.elements
.split_first_mut()
.expect("elements is empty");
for element in &*tail {
if element == first_element {}
}
}
}
The &*tail makes the mutable slice into an immutable one.
Mm.. nice trick, thanks. The problem is that in reality, I don't really have a Vec, but a Vec of Vecs (which explains why I called it Board). Also, I need to access the board by coordinates (x,y). So basically, for each cell in the board, I need to see if there's something in the surrounding cells. Something like in the game of life. Mm..