我正在学习用 Rust 编写的突破游戏教程,我有简单的数据结构来表示屏幕上的球:
pub struct Ball {
rect: Rect,
vel: Vec2,
}
它存储在向量中
let mut balls: Vec<Ball> = Vec::new();
但是,当我尝试计算球与球的碰撞时,我遇到错误:
--> 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);
}
}
}
我最初的方法是使用双重迭代,现在我知道借用检查器不允许我修改向量,因为它被认为是不安全的。我可以使用通用模式来解决此类问题吗?
您可以使用
split_at_mut
来实现此目的。感觉有点老套,但效果还不错。这是一个获取两个不同可变值的实现。
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]
}
playground上有一个工作版本。
然后您需要对数组长度进行双循环:
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();
...
}
}
请注意,我的循环确保我只访问每个无序对一次。
您可以使用
RefCell
实现可变性,并使用 iter()
代替 iter_mut()
,这样编译器就不会抱怨代码借用了 vec 两次,例如:
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;
}
}
split_at_mut
给你两片,所以你有两件事可以做。
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}");
}
}
}
这假设您的碰撞是传递的:如果您将 A 与 B 进行比较,则不需要同时将 B 与 A 进行比较。