How to mutably loop over vector in a nested loop

I want to implement collision on each enemy between each other. I want the enemies to hit each other and statically resolve the collision which means moving both enemy pairs once collision is found - mutable references.

for i in 0..enemies_len {
	let enemy = &mut self.enemies[i];	
	// Register enemy collision on player
	if enemy.rect.collision_other(&self.player.rect) {
		info!("Player dead!");
		return Ok(crate::State::GameOver)
	}
	for j in 0..enemies_len {
		let other_enemy = &self.enemies[j];
	}
		
	// Move enemies
	enemy.rect.offset_x(-enemy.rect.facing.cos() * dt as f32 * enemy.speed);
	enemy.rect.offset_y(-enemy.rect.facing.sin() * dt as f32 * enemy.speed);
}

This leads to having both mutable and immutable borrows.

error[E0502]: cannot borrow `self.enemies` as immutable because it is also borrowed as mutable
   --> src\GameStates\playing\mod.rs:167:24
    |
160 |             let enemy = &mut self.enemies[i];
    |                              ------------ mutable borrow occurs here
...
167 |                 let other_enemy = &self.enemies[j];
    |                                    ^^^^^^^^^^^^ immutable borrow occurs here
...
171 |             enemy.rect.offset_x(-enemy.rect.facing.cos() * dt as f32 * enemy.speed);
    |             ---------- mutable borrow later used here

How can this be accomplished?

Not that easy to do directly, given the "granularity" of Rust borrows; but you can use split_at_mut to circumvent it:

type ImplIteratorMut<'a, Item> =
    ::std::iter::Chain<
        ::std::slice::IterMut<'a, Item>,
        ::std::slice::IterMut<'a, Item>,
    >
;
trait SplitOneMut {
    type Item;

    fn split_one_mut (
        self: &'_ mut Self,
        i: usize,
    ) -> (&'_ mut Self::Item, ImplIteratorMut<'_, Self::Item>);
}

impl<T> SplitOneMut for [T] {
    type Item = T;
    
    fn split_one_mut (
        self: &'_ mut Self,
        i: usize,
    ) -> (&'_ mut Self::Item, ImplIteratorMut<'_, Self::Item>)
    {
        let (prev, current_and_end) = self.split_at_mut(i);
        let (current, end) = current_and_end.split_at_mut(1);
        (
            &mut current[0],
            prev.iter_mut().chain(end),
        )
    }
}

#[derive(Debug)]
struct Enemy(usize);

fn main ()
{
    let mut enemies: Vec<Enemy> = (0 .. 5).map(Enemy).collect();
    
    for i in 0 .. enemies.len() {
        let (enemy, other_enemies) = enemies.split_one_mut(i);
        dbg!(enemy);
        for other_enemy in other_enemies {
            let _: &mut Enemy = other_enemy;
            dbg!(other_enemy);
        }
    }
}
2 Likes

split_at_mut is your friend, in general, for this kind of thing.

The other thing you can do is build a separate queue of changes to apply -- which is parallelizable and only needs shared reads -- and apply all the collision resolutions afterwards. That's how some of the MIR optimizations work in rustc, for example:

https://github.com/rust-lang/rust/blob/f492693982d1e252f5411ae3e4d560ab0dfea48a/src/librustc_mir/transform/instcombine.rs#L23-L33

3 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.