1
fn update(&mut self, engine: &mut Engine, delta_time: f32){
    let game_state_ref = engine.get_game_state();
    let game_state = game_state_ref.borrow_mut();

    for entity in game_state.get_entities() {
        let ai_component = game_state.get_component_ai_mut(*entity).unwrap();
        ai_component.update(delta_time)
    }
}

The compiler won't let me borrow the game_state type Rc<RefCell<GameState>> reference as mutable. Does anyone have an idea on how to solve or work around this problem?

error[E0502]: cannot borrow `game_state` as mutable because it is also borrowed as immutable
  --> src/systems/ai_system.rs:27:32
   |
26 |         for entity in game_state.get_entities() {
   |                       -------------------------
   |                       |
   |                       immutable borrow occurs here
   |                       immutable borrow later used here
27 |             let ai_component = game_state.get_component_ai_mut(*entity).unwrap();
   |                                ^^^^^^^^^^ mutable borrow occurs here
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Teddy
  • 33
  • 8
  • It's hard to answer your question because it doesn't include a [MRE]. We can't tell what crates (and their versions), types, traits, fields, etc. are present in the code. It would make it easier for us to help you if you try to reproduce your error on the [Rust Playground](https://play.rust-lang.org) if possible, otherwise in a brand new Cargo project, then [edit] your question to include the additional info. There are [Rust-specific MRE tips](//stackoverflow.com/tags/rust/info) you can use to reduce your original code for posting here. Thanks! – Shepmaster Apr 13 '20 at 14:21
  • The given snippets point to a very common problem. [This](https://stackoverflow.com/questions/35936995/mutating-one-field-while-iterating-over-another-immutable-field) mighty be a duplicate problem. An approach to solve it was posted on the [baby steps blog](http://smallcultfollowing.com/babysteps/blog/2018/11/01/after-nll-interprocedural-conflicts/) – CoronA Apr 14 '20 at 06:38

3 Answers3

0

Unlike many other languages (ex. C++), Rust will not let you have shared state coupled with mutability. You can borrow something as immutable, and you can mutate something that is exclusively owned by you, but you cannot combine the two.

What Is Ownership?

nairware
  • 3,090
  • 9
  • 37
  • 58
0

Does anyone have an idea on how to solve...

You can either borrow immutably many times (x)or once mutably. I suggest you read the Rust book to get to understand the mechanics of borrowing before writing complex programs. !

See as well: Cannot borrow as mutable because it is also borrowed as immutable.

...or work around this problem?

The workaround would be to dereference raw pointers in unsafe code, but you should avoid that unless there is no other way of doing it. The borrow checker is likely why you want to use Rust to begin with!

Acorn
  • 24,970
  • 5
  • 40
  • 69
0

You provided very little context, but I would guess that your GameState, Entity and AiComponent look something like:

struct Entity {
    id: i32,
    // ...
}

struct AiComponent {
    // ...
}

impl AiComponent {
    pub fn update(&mut self, delta_time: f64) {
        // ...
    }
}

struct GameState {
    entities: Vec<Entity>,
    entity_id_to_ai: HashMap<i32, AiComponent>,
}

impl GameState {
    pub fn get_entities(&self) -> &[Entity] {
        &self.entities[..]
    }

    pub fn get_component_ai_mut(&mut self, entity: &Entity)
        -> Option<&mut AiComponent>
    {
        self.entity_id_to_ai.get_mut(&entity.id)
    }
}

With this API, Rust will not allow you to call get_component_ai_mut with entity borrowed from the same GameState instance. Why? Because, having &mut self, you can legally call self.entities.push(...) or even self.entities.clear(), which would invalidate the entity reference.

But it looks like you don't need the reference. Consider changing your API to:

impl GameState {
    pub fn iter_ai_components_mut(&mut self)
        -> impl Iterator<Item=&mut AiComponent>
    {
        self.entity_id_to_ai.values_mut()
    }
}

Then you can do:

for ai_component in game_state.iter_ai_components_mut() {
    ai_component.update(delta_time);
}
kreo
  • 2,613
  • 2
  • 19
  • 31
  • Thank you it worked. Mind if i ask why you use a hashmap instead of vec. when storing components? – Teddy Apr 13 '20 at 14:39
  • Just a hypothetical example, no particular reason behind it. You are right, Vec would do better in practice. – kreo Apr 13 '20 at 14:47