3

I want to iterate over a vector (or map) of actors in a world struct. The actors need access to the world as they might need to get information about the world, but can also change the state of the world. I should this be done properly in rust?

struct Actor {
}

impl Actor {
    pub fn step(&mut self, world: &mut World) {
        world.act();
    }
}

struct World {
    actors: Vec<Actor>,
    state: u32
}

impl World {
    pub fn step(&mut self) {
        for actor in self.actors.iter_mut() {
            actor.step(self);
        }
    }

    pub fn act(&mut self) {
        self.state += 1;
    }
}

fn main() {
    
}

This code give the error:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/main.rs:18:24
   |
17 |         for actor in self.actors.iter_mut() {
   |                      ----------------------
   |                      |
   |                      first mutable borrow occurs here
   |                      first borrow later used here
18 |             actor.step(self);
   |                        ^^^^ second mutable borrow occurs here

For more information about this error, try `rustc --explain E0499`.
error: could not compile `actors` due to previous error
H4kor
  • 1,552
  • 10
  • 28
  • 6
    You can't. Rust's borrow checker is protecting you from an entire category of problems here. What if the actor deletes itself from the world? What if it deletes another actor that comes before itself in the vector, causing itself to get moved to another location in memory? You'll have to rethink your design. Does the actor really need to be able to change everything and anything about the world? Can you maybe use a message queue, or return a list of mutations? – Thomas Dec 31 '22 at 15:02

2 Answers2

2

You can't. It is impossible to mutate something while iterating over it, and in your current layout, Actor::step() would have the power to modify the actors list. So your situation is impossible, and Rust is correct that it shouldn't be allowed.

The real question is: Does your step function really require the list of actors? If not, I would solve the situation by splitting the list of actors and the state into two objects. That way, you can iterate over actors while only modifying state:

struct Actor {}

impl Actor {
    pub fn step(&mut self, state: &mut WorldState) {
        state.act();
    }
}

struct WorldState {
    some_value: u32,
}

struct World {
    actors: Vec<Actor>,
    state: WorldState,
}

impl World {
    pub fn step(&mut self) {
        for actor in self.actors.iter_mut() {
            actor.step(&mut self.state);
        }
    }
}

impl WorldState {
    pub fn act(&mut self) {
        self.some_value += 1;
    }
}
Finomnis
  • 18,094
  • 1
  • 20
  • 27
0

A somewhat simple solution is to pass exactly what you need.

impl Actor {
    fn step(&mut self, state: &mut u32) {
        *state += 1;
    }
}

impl World {
    pub fn step(&mut self) {
        for actor in self.actors.iter_mut() {
            actor.step(&mut self.state);
        }
    }
}
Iggy
  • 4,767
  • 5
  • 23
  • 34