Question

I'm trying to create a vector to keep track of enemies in a game that will hold a bunch of mutable structs. I have a world struct that has the enemies as a member of it as follows:

pub struct World {
  pub player      : Creature,
  pub enemies     : Vec<Creature>,
}

I create the enemies vector as follows:

 let mut enemies = vec![];
 let mut enemy = Creature::new(5, 5, map_start_x+5, map_start_y+5, "$", 10, 1, "")
 enemies.push(enemy);

later on in the code when I pull out an individual enemy from enemies for attacking and try to update the hp as follows:

  for enemy in self.enemies.iter() {
    if new_x == enemy.x && new_y == enemy.y {
      enemy.hp -= self.player.damage;
      return;
    }
  }

which is where the problem occurs since enemy is apparently not mutable at this point

 error: cannot assign to immutable field `enemy.hp`
Was it helpful?

Solution

Updated for the latest Rust version

Mutability in Rust is not associated with data structures, it is a property of a place (read: a variable) where the data is stored. That is, if you place a value in a mutable slot:

let mut world = World::new();

then you can take mutable references to this variable, and hence call methods which can mutate it.

Your structure owns all data it contains, so you can make it mutable any time you need. self.enemies.iter() returns an iterator which generates items of type &Creature - immutable references, so you can't use them to mutate the structure. However, there is another method on a vector, called iter_mut(). This method returns an iterator which produces &mut Creature - mutable references, which do allow changing data they point to.

for enemy in self.enemies.iter_mut() {
  if new_x == enemy.x && new_y == enemy.y {
    enemy.hp -= self.player.damage;
    return;
  }
}

// The following also works (due to IntoIter conversion)
for enemy in &mut self.enemies {
  if new_x == enemy.x && new_y == enemy.y {
    enemy.hp -= self.player.damage;
    return;
  }
}

Note that in order for this to work, you have to pass self by mutable reference too, otherwise it will be impossible to mutate anything under it, implying that you won't be able to obtain mutable references to internals of the structure, including items of the vector. In short, make sure that whatever method contains this loop is defined like this:

fn attack(&mut self, ...) { ... }

OTHER TIPS

Vec<T>::iter() gives you an Iterator<&T> 1, i.e. an iterator over "immutable borrowed" pointers. If you want to mutate the contents, use Vec::iter_mut(), which is an Iterator<&mut T>.

In general, presence or absence of mut affects whether you can use some method, but not what each method means. This is not the whole truth (perhaps different traits with a method of the same name are implemented for & and &mut), but it's a good approximation.

1 I omitted the lifetime aspect of these types to make a point.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top