Given is an array of bodies that interact in some way with each other. As a newbie I approached it as I would do it in some other language:
struct Body {
x: i16,
y: i16,
v: i16,
}
fn main() {
let mut bodies = Vec::<Body>::new();
bodies.push(Body { x: 10, y: 10, v: 0 });
bodies.push(Body { x: 20, y: 30, v: 0 });
// keep it simple and loop only twice
for i in 0..2 {
println!("Turn {}", i);
for b_outer in bodies.iter() {
println!("x:{}, y:{}, v:{}", b_outer.x, b_outer.y, b_outer.v);
let mut a = b_outer.v;
for b_inner in bodies.iter() {
// for simplicity I ignore here to continue in case b_outer == b_inner
// just do some calculation
a = a + b_outer.x * b_inner.x;
println!(
" x:{}, y:{}, v:{}, a:{}",
b_inner.x,
b_inner.y,
b_inner.v,
a
);
}
// updating b_outer.v fails
b_outer.v = a;
}
}
}
Updating of b_outer.v after the inner loop has finished fails:
error[E0594]: cannot assign to immutable field `b_outer.v`
--> src/main.rs:32:13
|
32 | b_outer.v = a;
| ^^^^^^^^^^^^^ cannot mutably borrow immutable field
Making b_outer mutable:
for b_outer in bodies.iter_mut() { ...
doesn't work either:
error[E0502]: cannot borrow `bodies` as mutable because it is also borrowed as immutable
--> src/main.rs:19:32
|
16 | for b_outer in bodies.iter() {
| ------ immutable borrow occurs here
...
19 | for b_inner in bodies.iter_mut() {
| ^^^^^^ mutable borrow occurs here
...
33 | }
| - immutable borrow ends here
Now I'm stuck. What's the Rust approach to update b_outer.v after the inner loop has finished?
For what it's worth, I think the error message is telling you that your code has a logic problem. If you update the vector between iterations of the inner loop, then those changes will be used for subsequent iterations. Let's look at a smaller example where we compute the windowed-average of an array item and its neighbors:
[2, 0, 2, 0, 2] // input
[2/3, 4/3, 2/3, 4/3, 2/3] // expected output (out-of-bounds counts as 0)
[2/3, 0, 2, 0, 2] // input after round 1
[2/3, 8/9, 2, 0, 2] // input after round 2
[2/3, 8/9, 26/9, 0, 2] // input after round 3
// I got bored here
I'd suggest computing the output into a temporary vector and then swap them:
#[derive(Debug)]
struct Body {
x: i16,
y: i16,
v: i16,
}
fn main() {
let mut bodies = vec![Body { x: 10, y: 10, v: 0 }, Body { x: 20, y: 30, v: 0 }];
for _ in 0..2 {
let next_bodies = bodies
.iter()
.map(|b| {
let next_v = bodies
.iter()
.fold(b.v, { |a, b_inner| a + b.x * b_inner.x });
Body { v: next_v, ..*b }
})
.collect();
bodies = next_bodies;
}
println!("{:?}", bodies);
}
Output:
[Body { x: 10, y: 10, v: 600 }, Body { x: 20, y: 30, v: 1200 }]
If you really concerned about memory performance, you could create a total of two vectors, size them appropriately, then alternate between the two. The code would be uglier though.
As Matthieu M. said, you could use Cell or RefCell, which both grant you inner mutability:
use std::cell::Cell;
#[derive(Debug, Copy, Clone)]
struct Body {
x: i16,
y: i16,
v: i16,
}
fn main() {
let bodies = vec![
Cell::new(Body { x: 10, y: 10, v: 0 }),
Cell::new(Body { x: 20, y: 30, v: 0 }),
];
for _ in 0..2 {
for b_outer_cell in &bodies {
let mut b_outer = b_outer_cell.get();
let mut a = b_outer.v;
for b_inner in &bodies {
let b_inner = b_inner.get();
a = a + b_outer.x * b_inner.x;
}
b_outer.v = a;
b_outer_cell.set(b_outer);
}
}
println!("{:?}", bodies);
}
[Cell { value: Body { x: 10, y: 10, v: 600 } }, Cell { value: Body { x: 20, y: 30, v: 1200 } }]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With