0

I have this piece of code below where I iterate over a Vec (self.regions), and under the right conditions, I mutate the current element of the Vec

However, when processing a region, I must also call another method on self that borrows it, causing me to have both a mutable and shared reference of self at the same time, which is obviously impossible (and will not compile)

How can I change my code in order to make it work ?

I've thought of moving line 3 outside the loop and using a iter() instead of an iter_mut(), but then I don't see how to "keep the two iterators in sync" in order to make sure that both pieces of code refer to the same region

fn growth(&'a mut self) {
   for region in self.regions.iter_mut() {
       let candidates = self.neighbors(region); // Has to involve taking a &self
       // Do stuff with `candidates` that involves mutating `region`
   }
}

Edit

My post was lacking context, so here is the full function. self.regions is of type Vec<Vec<&'a Cell>>, and the signature of neighbors is fn neighbors(&self, region: &Vec<&Cell>) -> HashSet<&Cell>.

The objective of my program is to tesselate a grid into random polyominoes, by randomly selecting some sources and (the part that I posted) "growing them" into regions by iterating over each region and adding a random neighbor of the region to the region itself.

The neighbors function depends of the previous iterations' outcome, so it cannot be precomputed

    fn growth(&'a mut self) -> bool {
        let mut unchoosen_cells = 20; // More complex in reality
        let mut steps: usize = 0;

        while unchoosen_cells != 0 {
            if steps > GROWTH_STEP_LIMIT { return false; }
            for region in self.regions.iter_mut() {
                if rand::random() { continue; }

                let candidates = self.neighbors(region);
                if let Some(cell) = candidates.iter().choose(&mut thread_rng()) {
                    // cell.in_region = true;
                    region.push(cell);
                    unchoosen_cells -= 1;
                }
                steps += 1;
            }
        }
        true
    }
clino
  • 157
  • 6
  • 2
    You can't. Having a shared & a mutable reference to the same object is [undefined behavior (section on aliasing)](https://doc.rust-lang.org/reference/behavior-considered-undefined.html). You might have luck copying the region or candidates, but we're lacking context to be able to determine a solution. – cafce25 Dec 09 '22 at 16:22
  • Maybe the best way to solve your issue would be to [`take`](https://doc.rust-lang.org/std/mem/fn.take.html) the vector `self.regions` (which is relatively cheap, since `Vec::default()` does not allocate!), operate on it (which, maybe, won't interfere with `region`), then put it back into `self.regions`. – jthulhu Dec 09 '22 at 17:21
  • It seems obligatory at this point to point out that keeping references to `self` in `self.cells` is [very hard](https://stackoverflow.com/questions/32300132/why-cant-i-store-a-value-and-a-reference-to-that-value-in-the-same-struct) and you should probably look for some other solution. (I assume that's what `neighbors` is doing.) – cafce25 Dec 09 '22 at 17:45
  • @jthulhu i think that would work, but how do i put it back ? i've tried adding `mem::replace(&mut self.regions, regions);` just before the 2 possible code path ends, but i still get an error about having shared and mutable references at the same time (with the definition of `candidates`, even though the scopes are clearly distinct – clino Dec 09 '22 at 17:53
  • @clino then I think your design is just doomed. – jthulhu Dec 09 '22 at 18:00
  • You may want to refactor your code to use region _indices_ so that you can limit borrows from `self.regions` to the smallest possible scope. In the context of your `growth` function, this would mean iterating over the indices of `self.region` and making the `neighbors` function take in a region index rather than a region reference. – EvilTak Dec 09 '22 at 18:01

1 Answers1

0

After reading all the comments, I understand that my issue is the result of a poor design, and I will refactor my code in order to use indices instead of references

Thanks to all the people who took the time to help me, and I'm marking this as solved

clino
  • 157
  • 6