Smarter way to update my vector

Hi Rustaceans !

For learning purposes, I'm writing a Asteroid (arcade game) clone, with Rust and SDL2. Many things are working but I'm a bit stuck with a sensible part. I have got this code :

for (i,m) in missiles_entities.iter_mut().rev().enumerate()
                {
                    let r = InGame::test_missiles_collision(m, &poly_line_entities); 
                    if r.0 == true
                    {
                        let e : &Entity = &poly_line_entities[r.1];
                        builder::build_explosion(e.pos.x, e.pos.y,100, &mut point_entities, &mut rng);
                        poly_line_entities.swap_remove(r.1);
                        missiles_entities.swap_remove(i);
                    }
                }

My problem is that I'm iterating over the 'missiles_entities' vector to determine collision against a vector of asteroid. The method test_missiles_collision returns a tuple (bool, usize) indicating if a collision was found, and the index of the Asteroid into it's vector, for removal. In case of a collision the actual missile, should be removed from the vector I' m iterating on, to avoid other further collision.

The compiler complains about double mutable borrow:

error[E0499]: cannot borrow `missiles_entities` as mutable more than once at a time
   --> src/in_game.rs:196:24
    |
183 |                 for (i,m) in missiles_entities.iter_mut().rev().enumerate()
    |                              ----------------------------------------------
    |                              |
    |                              first mutable borrow occurs here
    |                              first borrow later used here
...
196 |                        missiles_entities.swap_remove(i);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second mutable borrow occurs here

This is fine and I understand the reason. As a work around, I could store in another vector a the list of missiles to remove. It worked, but I' m pretty sure that there is a smarter way to do it. I heard about the "Don t mutate a vector you are iterating on" :slight_smile:

Best regards

You can use the Vec::retain_mut method, although you'll have to re-implement the logic of enumerate().

3 Likes

geeklint's suggestion is good, though I think plain Vec::retain would work as well.

As an aside, I'd recommend using Option rather than (bool, usize). Then the if becomes

if Some(asteroid_index) = r {
3 Likes

Thanks both of you for your fast and valuable answer. It worked nicely. I implemented @geeklint solution, with @joonazan non mutable solution. For those interested in the implementation I put it here, but keep in mind that it's Rust beginner' s code. Don't hesitate to correct it.

                 let closure = |m: &Entity| -> bool {
                    let r = InGame::test_missiles_collision(m, &poly_line_entities);
                    if let Some(index) = r
                    {
                        let e: &Entity = &poly_line_entities[index];
                        builder::build_explosion(e.pos.x, e.pos.y, 100, &mut point_entities, &mut rng);
                        poly_line_entities.swap_remove(index);
                        return false;
                    }
                    true
                };

                missiles_entities.retain(closure);

To be honest, I need to read again the book, on lambda paragraph, to fully understand what' s visible from this function.

Thanks !

1 Like