Iterating over a self.some_vec while mutating self

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

struct Foo {
    field: usize,
    some_vec: Vec<usize>
}

impl Foo {
    fn mut_with_item(&mut self, item: usize) {
        self.field = self.field + item
    }
    
    fn mut_per_item(&mut self){
        for &item in &self.some_vec {
            self.mut_with_item(item)
        }
    }
}

Playground

results in the error:

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?

One option is to split Foo like this:

struct Foo {
    sub: SubFoo,
    some_vec: Vec<usize>
}

struct SubFoo {
    field: usize,
}

impl Foo {
    fn mut_per_item(&mut self){
        for &item in &self.some_vec {
            self.sub.mut_with_item(item)
        }
    }
}

impl SubFoo {
    fn mut_with_item(&mut self, item: usize) {
        self.field = self.field + item
    }
}

Another option is to use interior mutability. (this is more complicated for non-Copy types)

use std::cell::Cell;

struct Foo {
    field: Cell<usize>,
    some_vec: Vec<usize>
}

impl Foo {
    fn mut_with_item(&self, item: usize) {
        self.field.set(self.field.get() + item);
    }
    
    fn mut_per_item(&mut self){
        for &item in &self.some_vec {
            self.mut_with_item(item)
        }
    }
}
1 Like

I see, thanks - seems it will work for me.
Do these solutions impact performance in some way? Is one of these preferred over the other?

The performance should be identical, at least in release mode.

Generally both sub-structs and Cell have no runtime cost.

2 Likes

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;
        }
    }
1 Like

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.

2 Likes

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:

  1. 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.
  2. 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?

In that case you can split the slice into two:

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..

Consider one of these:

  1. Use indexes rather than an iterator.
  2. Make the modifications after the loop, when the borrow has finished.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.