Help me with the "struct of vecs" pattern

So I'm trying to work with a stuct that contains a series of Vecs, some of which I iterate over (in this context immutably), but while I do this I need to modify another Vec included in the struct.

A lot of people have been talking about the "struct of vecs" pattern and how it is used in "data driven programming." Basically I'm trying to do that.

My iterators are complicated. That said, they only work over the immutable vecs. (At least immutable in this function. The get mutated in other functions.)

Here is a super-simplified version of what I want to do. I understand why it fails, but there must be some nice way to work with structs like this. How do I make this work?

(My actual iterator is much more complex than this. I cannot simplify it.)


#[derive(Copy, Clone)]
struct Mary;

struct Fred {
    a: Vec<Mary>,
    b: Vec<Mary>,
}

impl Fred {

    fn mary_iter(&self) -> MaryIter {
        MaryIter {
          mary_slice: &self.a,
          idx: 0,
        }
    }

    fn do_thing(&mut self) {
        for i in self.mary_iter() {
            self.b.push(i);
        }
    }
}

struct MaryIter<'a> {
    mary_slice: &'a [Mary],
    idx: usize,
}

impl<'a> Iterator for MaryIter<'a> {
    type Item = Mary;
    fn next(&mut self) -> Option<Mary> {
        if self.idx == self.mary_slice.len() {
            None
        } else {
            let result = self.mary_slice[self.idx];
            self.idx += 1;
            Some(result)
        }
    }
}

fn main() {

}

(Playground)

Errors:

   Compiling playground v0.0.1 (file:///playground)
error[E0502]: cannot borrow `self.b` as mutable because `*self` is also borrowed as immutable
  --> src/main.rs:21:13
   |
20 |         for i in self.mary_iter() {
   |                  ----           - immutable borrow ends here
   |                  |
   |                  immutable borrow occurs here
21 |             self.b.push(i);
   |             ^^^^^^ mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: Could not compile `playground`.

To learn more, run the command again with --verbose.

I'm considering doing some gross trickery using mem::replace to extract the vectors I need to mutate, mutate them, and them put them back, but that seems wrong somehow.

Make mary_iter a free function that takes a &[Mary] rather than a &Fred.

Alternately, separate the function into distinct scan and update phases. Build up a list of changes to make first, release the borrow, then perform the updates.

I tried the first and still got the error.

I don't want to do the second because I don't want to malloc up space if I can help it. If I try to put a "scratchspace" vector in the struct, I have the same problem. If I make the caller pass in an empty (but with capacity) vec -- well that is ugly.

Oh and the iterator is complex. It actually takes several immutable vecs and uses other logic from the main "impl Fred" section to track everything.

It works fine.

Also works fine.

So the free function will need multiple arguments. You might need to make other methods into free functions so they can be called without a complete instance.

If you're looking for a clean solution that doesn't require repeating yourself and fully encapsulates what you're doing without awkward interfaces, you're probably going to be bitterly disappointed. Rust doesn't really allow you to encapsulate these sorts of constructs while retaining optimal performance and memory usage and a clean API.

You're going to have to compromise somewhere.

You can split the borrow with something like this:

fn mary_iter(&mut self) -> (MaryIter, &mut Vec<Mary>) {
        (
            MaryIter {
                mary_slice: &self.a,
                idx: 0,
            },
            &mut self.b,
        )
    }

    fn do_thing(&mut self) {
        let (iter, b) = self.mary_iter();
        for i in iter {
            b.push(i);
        }
    }

You can also rearrange code so that a and b don’t live in the same struct. Generally, the approach you tried with an immutable borrow taken from a method that lasts while trying to mutate something in self as well won’t work well in Rust.

Thanks for the suggestions.

For now I'm just hacking it up with mem::replace, just to keep making progress. I'll think about it though.