Tomasz Bawor
Tomasz Bawor

Reputation: 1647

Two mutable borrows from vector

I am following a tutorial on breakout game written in rust and I have simple data structure representing balls on the screen:

pub struct Ball {
    rect: Rect,
    vel: Vec2,
}

It is stored in vector

let mut balls: Vec<Ball> = Vec::new();

However when I try to calculate ball to ball collision I encounter error:

 --> src/main.rs:193:31
    |
192 |         for ball in balls.iter_mut() {
    |                     ----------------
    |                     |
    |                     first mutable borrow occurs here
    |                     first borrow later used here
193 |             for other_ball in balls.iter_mut() {
    |                               ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
// ball collision with balls
        for ball in balls.iter_mut() {
            for other_ball in balls.iter_mut() {
                if ball != other_ball {
                    resolve_collision(&mut ball.rect, &mut ball.vel, &other_ball.rect);
                }
            }
        }

My initial approach was to use double iteration, now I know that borrow checker wont allow me to modify vector as it is considered unsafe. Is there a common pattern that I could use to solve this kind of issues?

Upvotes: 3

Views: 1181

Answers (3)

Bryan Larsen
Bryan Larsen

Reputation: 10006

split_at_mut gives you two slices, so you have two things you can take a mut on.

pub fn main() {
    let mut data = vec!["a", "b", "c", "d", "e"];
   
    for i in 1..data.len() {
        let (left, right) = data.split_at_mut(i);
        let a = &mut right[0];
        for b in left.iter_mut() {
            println!("{a} {b}");
        }
    }
}

This assumes that your collisions are transitive: if you compare A with B, you don't need to also compare B with A.

If they aren't transitive, iterate both left & right:

    for i in 1..data.len() {
      let (left, right) = data.split_at_mut(i);
      
      {   // contain the mut to this scope
        let a = &mut right[0];
        for b in left.iter_mut() {
            println!("{a} {b}");
        }
      }

      {   // new scope for new mut's       
        let a = &mut left[i-1];
        for b in right.iter_mut() {
            println!("{a} {b}");
        }
      }
    }

Upvotes: 0

fred xia
fred xia

Reputation: 149

You can use RefCell for mutability and iter() instead of iter_mut() so that compiler won't complain that the code borrows the vec twice, e.g.:

struct Ball(u32, u32);

let mut balls = vec![];

balls.push(RefCell::new(Ball(0, 0)));
// push more balls into vec

for b1 in balls.iter() {
    for b2 in balls.iter() {
        // change attributes of a ball
        b1.borrow_mut().0 = 10;
        b2.borrow_mut().1 = 20;
    }
}

Upvotes: 1

Michael Anderson
Michael Anderson

Reputation: 73460

You can achieve this using split_at_mut. It feels a bit hacky, but works OK. Here's an implementation that gets two different mutable values.

pub fn get_mut2<T>(v: &mut [T], i: usize, j: usize) -> Option<(&mut T, &mut T)> {
    if i == j {
        return None;
    }
    let (start, end) = if i < j { (i, j) } else { (j, i) };

    let (first, second) = v.split_at_mut(start + 1);
    Some((&mut first[start], &mut second[end - start - 1]))
}

pub fn main() {
    let mut data = [0, 1, 2, 3, 4, 5, 6, 7];
    let (a, b) = get_mut2(&mut data, 3, 6).unwrap();
    *a += 10;
    *b += 10;
    eprintln!("{:?}", data); // [0, 1, 2, 13, 4, 5, 16, 7]
}

There's a working version on the playground.

You'd then need a double loop over your array lengths:

assert!(!a.is_empty());
for i in 0..a.len()-1 {
  for j in i..a.len() {
    let (ball_i, ball_j) = get_mut2(&mut a, i, j).unwrap();
    ...
  }
}

Note that my loop ensures I only visit each unordered pair once.

Upvotes: 1

Related Questions