0

I'm still a bit new to Rust, so I'm not quite sure how to restructure my code properly to make what I'm trying to do possible. Here's a link to an MCVE of the code I'm running.

Essentially what I'm trying to do is iterate through a vector of entities and get an action from each of them. I don't need a mutable borrow for that part specifically, but I do need to pass a mutable reference to self into a method later in the function to perform the action that was returned.

Here's the exact error message I'm getting:

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
  --> src/main.rs:16:72
   |
16 |             let action = self.entities[self.current_entity].get_action(self);
   |                          -------------                      ---------- ^^^^ immutable borrow occurs here
   |                          |                                  |
   |                          |                                  mutable borrow later used by call
   |                          mutable borrow occurs here

error: aborting due to previous error

How should I structure my code so that what I'm trying to do is possible?

Steampunkery
  • 3,839
  • 2
  • 19
  • 28
  • @SCappella: Wow, that's super helpful. What's even better is it also taught me why it's not allowed, alongside several workable solutions. I'm heading off to bed, but once I get back to the code I'll post whatever I end up using for posterity, if there isn't a working answer by then. Which of those solutions do you think is the best? – Steampunkery Sep 29 '19 at 06:00

1 Answers1

1

I split up line 16 from your example to explain what (I think) is going on:

I.e. from

let action = self.entities[self.current_entity].get_action(self);

to:

let entity = &mut self.entities[self.current_entity];
let action = entity.get_action(self);

In the above, entity mutably refers to self (indirectly through self.entities). Thus, get_action cannot modify self because it could - in particular - change self.entities, thereby invalidating the reference entity. This is not allowed by (safe) Rust.

You could try to split up your Level into entities and noentities. This allows you to explicitly specify which parts of Level are actually mutably referred to. (See https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a8199f0bd4f8119f2ec1e79f9ebb542d for an updated example.)

I.e. you'd have

struct LevelNoEntities {
    current_entity: usize,
    foo: i32,
}
struct Level {
    entities: Vec<Entity>,
    noentities: LevelNoEntities,
}

Then, you would now have the following:

let entity = &mut self.entities[self.noentities.current_entity];
let action = entity.get_action(&self.noentities);

Now, entity only refers to self.entities, and you can still pass noentities around because the compiler now knows you mutably refer to only a part of Level.

phimuemue
  • 34,669
  • 9
  • 84
  • 115