Reputation: 1647
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
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
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
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