I am learning Rust and recently went through an exercise where I had to iterate through numbers that could go in either direction. I tried the below with unexpected results.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn test() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = p1.x..=p2.x;
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y = p1.y..=p2.y;
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
let points: Vec<Point> = all_x.zip(all_y).map(|(x, y)| Point { x, y }).collect();
println!("points: {:?}", points);
}
The output was
all_x: [1, 2, 3]
all_y: []
points: []
After some googling I found an explanation and some old answers which basically amount to use (a..b).rev() as needed.
My question is, how do I do this in a dynamic way? If I use an if...else like so
let all_x = if p1.x < p2.x { (p1.x..=p2.x) } else { (p2.x..=p1.x).rev() };
I get a type error because the else is different than the if
|
58 | let all_x = if p1.x < p2.x { (p1.x..=p2.x) }
| - ------------- expected because of this
| _________________|
| |
59 | | else { (p2.x..=p1.x).rev() };
| |____________^^^^^^^^^^^^^^^^^^^_- `if` and `else` have incompatible types
| |
| expected struct `RangeInclusive`, found struct `Rev`
|
= note: expected type `RangeInclusive<_>`
found struct `Rev<RangeInclusive<_>>`
After trying a bunch of different variations on let all_x: dyn Range<Item = i32>, let all_x: dyn Iterator<Item = i32>, etc, the only way I have managed to do this is by turning them into collections and then back to iterators.
let all_x: Vec<i32>;
if p1.x < p2.x { all_x = (p1.x..=p2.x).collect(); }
else { all_x = (p2.x..=p1.x).rev().collect(); }
let all_x = all_x.into_iter();
println!("all_x: {:?}", all_x.clone().collect::<Vec<i32>>());
let all_y: Vec<i32>;
if p1.y < p2.y { all_y = (p1.y..=p2.y).collect(); }
else { all_y = (p2.y..=p1.y).rev().collect(); }
let all_y = all_y.into_iter();
println!("all_y: {:?}", all_y.clone().collect::<Vec<i32>>());
which provides the desired outcome
all_x: [1, 2, 3]
all_y: [8, 7, 6]
points: [Point { x: 1, y: 8 }, Point { x: 2, y: 7 }, Point { x: 3, y: 6 }]
but is a bit repetitive, inelegant and I'm assuming not very efficient at large numbers. Is there a better way to handle this situation?
NOTE: Sorry for including the Point struct. I could not get my example to work with x1, x2, etc. Probably a different question for a different post lol.
You can dynamically dispatch it. Wrapping them into a Box and returning a dynamic object, an Iterator in this case. For example:
fn maybe_reverse_range(init: usize, end: usize, reverse: bool) -> Box<dyn Iterator<Item=usize>> {
if reverse {
Box::new((init..end).rev())
} else {
Box::new((init..end))
}
}
Playground
The enum itertools::Either can be used to solve the incompatible type error in the if/else statement. A function like get_range_iter below using Either can reduce the code repetition.
use itertools::Either;
fn get_range_iter(start: i32, end: i32) -> impl Iterator<Item=i32> {
if start < end {
Either::Left(start..=end)
} else {
Either::Right((end..=start).rev())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct Point {
x: i32,
y: i32
}
fn main() {
let p1 = Point { x: 1, y: 8 };
let p2 = Point { x: 3, y: 6 };
let all_x = get_range_iter(p1.x, p2.x);
let all_y = get_range_iter(p1.y, p2.y);
println!("all_x: {:?}", all_x.collect::<Vec<_>>());
println!("all_y: {:?}", all_y.collect::<Vec<_>>());
}
Playground
Related
CONTEXT
General overview
(Here is the github page with the minimal example of my problem, and the page of my whole project)
I'm very new to rust and I'm trying to simulate the behavior of a fluid in Rust. This is easy: computing large arrays with some functions for each timestep.
I'd like to parallelize the computations done each timestep using rayon. But the compiler doesn't want me to access a mutable struct containing an Array that I want to modify, even if I'm sure that each modification will be on different places in the array: which assure me that it's safe. (I think?).
So my question is: should I use unsafe rust in here? If so, how?
And: is it possible to make Rust understand that it's safe, or to do it properly without unsafe rust?
I saw this post which kind of resembled my problem, but couldn't find a way to use the solution for my problem.
I also tried to put unsafe {...} keywords everywhere, but the compilator still complains...
You may only need to read "Structs" subsection to understand the problem, but I will also put a "Function" subsection, in case it can be important. If you think it might not be necessary, you can skip to "Main function" subsection.
Structs
Here are my structs:
I'd like to keep them that way, as they would give me (I think) more flexibility with setters/getters, but I'm open to change the way it's implemented right now.
#[derive(Debug, PartialEq)]
struct vec2D {pub x: f64, pub y: f64}
#[derive(Debug, PartialEq)]
struct ScalarField2D {
s: Array2<f64>,
}
#[derive(Debug, PartialEq)]
struct VectorField2D {
x: ScalarField2D,
y: ScalarField2D
}
impl ScalarField2D {
// also a constructor new() but not shown for simplicity
fn get_pos(&self, x: usize, y: usize) -> f64
{return self.s[[y,x]];}
fn set_pos(&mut self, x: usize, y: usize, f: f64)
{self.s[[y,x]] = f;}
}
impl VectorField2D {
// also a constructor new() but not shown for simplicity
fn get_pos(&self, x: usize, y: usize) -> vec2D
{let vec_at_pos = vec2D {
x: self.x.get_pos(x, y),
y: self.y.get_pos(x, y)};
return vec_at_pos;}
fn set_pos(&mut self, x: usize, y: usize, vec: &vec2D)
{self.x.set_pos(x, y, vec.x);
self.y.set_pos(x, y, vec.y);}
}
Function
Here is my function: which takes a ScalarField2D struct, and computes a vector called the "gradient" at a particular position of the ScalarField2D array, and then returning this vector as a "vec2D" struct.
// computes the gradient of a scalar field at a given position
fn grad_scalar(a: &ScalarField2D,
x: i32, y: i32,
x_max: i32, y_max: i32) -> vec2D
{
let ip = ((x+1) % x_max) as usize;
// i-1 with Periodic Boundaries
let im = ((x - 1 + x_max) % x_max) as usize;
// j+1 with Periodic Boundaries
let jp = ((y+1) % y_max) as usize;
// j-1 with Periodic Boundaries
let jm = ((y - 1 + y_max) % y_max) as usize;
let (i, j) = (x as usize, y as usize);
let grad = vec2D {
x: (a.get_pos(ip, j) - a.get_pos(im, j))/(2.),
y: (a.get_pos(i, jp) - a.get_pos(i, jm))/(2.)};
return grad;
}
Main function
Here is my problem:
I try to iterate over all the possible x and y using (0..x_max).into_par_iter() (or y_max for y), compute the gradient associated with each position, and then set the value to the ScalarField2D struct using the set_pos method... Here is the main function, and the import commands, and I will show you the error message I get in the next subsection
use ndarray::prelude::*;
use rayon::prelude::*;
fn main() {
let (x_max, y_max) = (2usize, 50usize);
let (x_maxi32, y_maxi32) = (x_max as i32, y_max as i32);
let mut GD_grad_rho = VectorField2D::new(x_max, y_max);
let mut GD_rho = ScalarField2D::new(x_max, y_max);
let x_iterator = (0..x_max).into_par_iter();
x_iterator.map(|xi| {
let y_iterator = (0..y_max).into_par_iter();
y_iterator.map(|yi| {
// unsafe here?
GD_grad_rho
.set_pos(xi, yi,
&grad_scalar(&GD_rho,
xi as i32, yi as i32,
x_maxi32, y_maxi32));
});});
}
Error message
Here is the compilation error I get
error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:104:13
|
104 | / GD_grad_rho
105 | | .set_pos(xi, yi,
106 | | &grad_scalar(&GD_rho,
107 | | xi as i32, yi as i32,
108 | | x_maxi32, y_maxi32));
| |__________________________________________________________^ cannot borrow as mutable
error[E0596]: cannot borrow `GD_grad_rho` as mutable, as it is a captured variable in a `Fn` closure
--> src/main.rs:101:24
|
101 | y_iterator.map(|yi| {
| ^^^^ cannot borrow as mutable
...
104 | GD_grad_rho
| ----------- mutable borrow occurs due to use of `GD_grad_rho` in closure
For more information about this error, try `rustc --explain E0596`.
error: could not compile `minimal_example_para` due to 2 previous errors
If you want the complete thing, I created a github repo with everything in it.
Tests after susitsm answer
So I did something like this:
use ndarray::prelude::*;
use rayon::prelude::*;
fn grad_scalar(a: &Array2<f64>,
i: usize, j: usize) -> (f64, f64)
{
let array_shape = a.shape();
let imax = array_shape[0];
let jmax = array_shape[1];
// i-1 with Periodic Boundaries
let ip = ((i + 1) % imax);
// i-1 with Periodic Boundaries
let im = ((i + imax) - 1) % imax;
// j+1 with Periodic Boundaries
let jp = ((j + 1) % jmax);
// j-1 with Periodic Boundaries
let jm = ((j + jmax) - 1) % jmax;
let grad_i = (a[[ip, j]] - a[[im, j]])/2.;
let grad_j = (a[[i, jp]] - a[[i, jm]])/2.;
return (grad_i, grad_j);
}
fn main() {
let a = Array::<f64, Ix2>::ones((dim, dim));
let index_list: Vec<(_, _)> = (0..a.len())
.into_par_iter()
.map(|i| (i / a.dim().0, i % a.dim().1))
.collect();
let (r1, r2): (Vec<_>, Vec<_>) = (0..a.len())
.into_par_iter()
.map(|i| (index_list[i].0, index_list[i].1))
.map(|(i, j)| grad_scalar(&a, i, j))
.collect();
let grad_row = Array2::from_shape_vec(a.dim(), r1).unwrap();
let grad_col = Array2::from_shape_vec(a.dim(), r2).unwrap();
}
Which gives me the result I want, even though I wanted to access the values through my Structs. Which is not exactly what I want but we're getting closer
But I wonder about the efficiency for more arrays, I'll post a separate question for that
You can do something like this:
use ndarray::Array2;
use rayon::prelude::*;
fn main() {
let a: Vec<u64> = (0..20000).collect();
let a = Array2::from_shape_vec((100, 200), a).unwrap();
let stuff = |arr, i, j| (i + j, i * j);
let (x, y): (Vec<_>, Vec<_>) = (0..a.len())
.into_par_iter()
.map(|i| (i / a.dim().0, i % a.dim().1))
.map(|(i, j)| stuff(&a, i, j))
.collect();
let grad_x = Array2::from_shape_vec(a.dim(), x);
let grad_y = Array2::from_shape_vec(a.dim(), y);
let grad_vector_field = VectorField2D {
x: ScalarField2D { s: grad_x },
y: ScalarField2D { s: grad_y },
};
}
Or implement the FromParallelIterator<vec2D>
impl FromParallelIterator<vec2D> for VectorField2D {
fn from_par_iter<I>(par_iter: I) -> Self
where I: IntoParallelIterator<Item = T>
{
let (x, y): (Vec<_>, Vec<_>) = par_iter
.into_par_iter()
.map(|vec_2D| {
let vec2D { x, y } = vec_2D;
(x, y)
})
.collect();
Self {
x: ScalarField2D { s: Array2::from_shape_vec(a.dim(), x) },
y: ScalarField2D { s: Array2::from_shape_vec(a.dim(), y) },
}
}
}
This will enable using collect for your type when using parallel iterators
let vector_field: VectorField2D = (0..a.len())
.into_par_iter()
.map(|i| (index_list[i].0, index_list[i].1))
.map(|(i, j)| grad_scalar(&a, i, j))
.collect();
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?
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.
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;
}
}
I'm trying to return sum of 3 largest numbers in an array like this:
fn max_tri_sum(arr: &[i32]) -> i32 {
arr.sort();
arr[0]+arr[1]+arr[2]
}
but I keep getting this error:
error[E0596]: cannot borrow `*arr` as mutable, as it is behind a `&` reference
fn max_tri_sum(arr: &[i32]) -> i32 {
------ help: consider changing this to be a mutable reference: `&mut [i32]`
arr.sort();
^^^ `arr` is a `&` reference, so the data it refers to cannot be borrowed as mutable
I shouldn't change arr: &[i32] to arr: &mut [i32] because of some restrictions. So what can I do about it?
P.S: I tried to clone arr to a mutable variable but got other errors:
fn max_tri_sum(arr: &[i32]) -> i32 {
let a: &mut [i32] = *arr.clone();
a.sort();
a[0]+a[1]+a[2]
}
You could also use a BinaryHeap to store the three largest values so far and replace the smallest value while looping through the array:
use std::collections::BinaryHeap;
fn max_tri_sum(arr: &[i32]) -> i32 {
let mut heap = BinaryHeap::new();
heap.push(-arr[0]);
heap.push(-arr[1]);
heap.push(-arr[2]);
for e in arr[3..].iter() {
if -e < *heap.peek().unwrap() {
heap.pop();
heap.push(-e);
}
}
-heap.drain().sum::<i32>()
}
Or if you prefer the sort option, you can convert the slice to a vector:
fn max_tri_sum(arr: &[i32]) -> i32 {
let mut arr1 = arr.to_vec();
arr1.sort_by(|a, b| b.cmp(a));
arr1[0] + arr1[1] + arr1[2]
}
Playground
In Rust, you can't have a variable that has both reference and mutable reference in the same scope.
You have several choices :
You could simply do a loop on the slice to get the values you want. Something like this (there's probably a more elegant way with iterators)
fn max_tri_sum(arr: &[i32]) -> i32 {
let mut maxes = [0, 0, 0];
for &el in arr {
if el > maxes[0] && el < maxes[1] {
maxes[0] = el;
} else if el > maxes[1] && el < maxes[2] {
if maxes[1] > maxes[0] {
maxes[0] = maxes[1];
}
maxes[1] = el;
} else if el > maxes[2] {
if maxes[2] > maxes[1] {
maxes[1] = maxes[2];
}
maxes[2] = el;
}
}
maxes[0] + maxes[1] + maxes[2]
}
You can create a new Vec from the slice, and then do all the operations on it (which requires allocation, but should be fine for small Vecs).
fn max_tri_sum(arr: &[i32]) -> i32 {
let mut arr = Vec::from(arr);
arr.sort();
arr[0] + arr[1] + arr[2]
}
I would also like to point that sort sorts from smallest to biggest, so the index 0, 1, 2, ... would be the smallest values in the array, which I don't think is what you want to do!
I am implementing a general matrix solver. In doing so I make use of the "Not" operator to get around another issue which I'll explain below. However, when invoking the function in my tests, I get the following error:
error[E0271]: type mismatch resolving `<i32 as std::ops::Not>::Output == bool`
--> src/matrix.rs:223:15
|
90 | pub fn reduce<T>(mat: &mut Matrix<T>) -> Result<Matrix<T>, &'static str>
| ------
...
97 | + Not<Output = bool>
| ------------- required by this bound in `matrix::reduce`
...
223 | let res = reduce(&mut mat).unwrap();
| ^^^^^^ expected i32, found bool
error: aborting due to previous error
This is particularly confusing because I am not sure how else I would implement the Not trait and have it function properly. When bool is the output type, it compiles just fine but seems to bark during execution.
Here's my code:
/// Performs a reduction operation on a given matrix, giving the reduced row echelon form
pub fn reduce<T>(mat: &mut Matrix<T>) -> Result<Matrix<T>, &'static str>
where
T: num_traits::Zero
+ num_traits::One
+ Mul<T, Output = T>
+ Add<T, Output = T>
+ Sub<T, Output = T>
+ Not<Output = bool>
+ Neg<Output = T>
+ Div<T, Output = T>
+ Copy,
{
let exchange = |matrix: &mut Matrix<T>, i: usize, j: usize| {
matrix.data.swap(i, j);
};
let scale = |matrix: &mut Matrix<T>, row: usize, factor: T| {
for i in 0..matrix.data[row].len() {
matrix.data[row][i] = matrix.data[row][i] * factor;
}
};
let row_replace = |matrix: &mut Matrix<T>, i: usize, j: usize, factor: T| {
for k in 0..matrix.data[j].len() {
matrix.data[j][k] = matrix.data[j][k] + (matrix.data[i][k] * factor);
}
};
// Reduction steps
let n = mat.data.len();
for i in 0..n {
// Find a pivot point
for j in i..n {
if !mat.data[j][i] { // <------- Error Here *********
if i != j {
exchange(mat, i, j);
break;
}
}
if j == n - 1 {
return Err("No pivot found")
}
}
// Put zeros below diagonal
for j in i + 1..n {
row_replace(mat, i, j, -mat.data[j][i] / mat.data[i][i]);
}
}
// Back substitution (bottom up)
for i in (0..n - 1).rev() {
for j in 0..i {
row_replace(mat, i, j, -mat.data[j][i] / mat.data[i][i]);
}
}
// Add 1's to the diagonal
for i in 0..n {
scale(mat, i, T::one() / mat.data[i][i]);
}
Ok(mat.clone())
}
#[test]
fn it_row_reduces() {
let mat = Matrix {
data: vec![vec![2, 1, 4], vec![1, 2, 5]],
nrows: 2,
ncols: 3,
};
let comp = Matrix {
data: vec![vec![1, 0, 1], vec![0, 1, 2]],
nrows: 2,
ncols: 3,
};
let res = reduce(&mut mat).unwrap();
assert_eq!(res.data, comp.data);
}
Originally, the code looked like the following:
if mat.data[j][i] != T::zero() {
if i != j {
exchange(mat, i, j);
break;
}
}
But it seems that even with the Not trait added to the function signature, this operation would never work, giving the following error:
binary operation `!=` cannot be applied to type `T`: T
I'm looking to figure out where I'm going wrong with this code and if my use of generics for this comparison is the most idiomatic way to do it in rust. Any additional feedback is appreciated. I can provide the struct as well, I just wanted to keep the question as brief as possible.
In Rust, ! serves as both logical NOT and bitwise NOT, depending on the argument type. It performs a logical NOT when the argument is a bool and a bitwise NOT when the argument is an integer type. The only built-in type that implements Not<Output = bool> is bool.
You should stick to if mat.data[j][i] != T::zero() {. != is provided by the PartialEq trait. Instead of the T: Not<Output = bool> bound, you'll want T: PartialEq<T> or simply T: Eq.
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 } }]
I know the question is like 2 years old, but I got curious about it.
This C# program produces the original desired output:
var bodies = new[] { new Body { X = 10, Y = 10, V = 0 },
new Body { X = 20, Y = 30, V = 0 } };
for (int i = 0; i < 2; i++)
{
Console.WriteLine("Turn {0}", i);
foreach (var bOuter in bodies)
{
Console.WriteLine("x:{0}, y:{1}, v:{2}", bOuter.X, bOuter.Y, bOuter.V);
var a = bOuter.V;
foreach (var bInner in bodies)
{
a = a + bOuter.X * bInner.X;
Console.WriteLine(" x:{0}, y:{1}, v:{2}, a:{3}", bInner.X, bInner.Y, bInner.V, a);
}
bOuter.V = a;
}
}
Since only v is ever changed, we could change the struct to something like this:
struct Body {
x: i16,
y: i16,
v: Cell<i16>,
}
Now I'm able to mutate v, and the program becomes:
// keep it simple and loop only twice
for i in 0..2 {
println!("Turn {}", i);
for b_outer in bodies.iter() {
let mut a = b_outer.v.get();
println!("x:{}, y:{}, v:{}", b_outer.x, b_outer.y, a);
for b_inner in bodies.iter() {
a = a + (b_outer.x * b_inner.x);
println!(
" x:{}, y:{}, v:{}, a:{}",
b_inner.x,
b_inner.y,
b_inner.v.get(),
a
);
}
b_outer.v.set(a);
}
}
It produces the same output as the C# program above. The "downside" is that whenever you want to work with v, you need use get() or into_inner(). There may be other downsides I'm not aware of.
I decided to split the structure in one that is used as a base for the calculation (input) in the inner loop (b_inner) and one that gathers the results (output). After the inner loop finished, the input structure is updated in the outer loop (b_outer) and calculation starts with the next body.
What's now not so nice that I have to deal with two structures and you don't see their relation from the declaration.
#[derive(Debug)]
struct Body {
x: i16,
y: i16,
}
struct Velocity {
vx: i16,
}
fn main() {
let mut bodies = Vec::<Body>::new();
let mut velocities = Vec::<Velocity>::new();
bodies.push(Body { x: 10, y: 10 });
bodies.push(Body { x: 20, y: 30 });
velocities.push(Velocity { vx: 0 });
velocities.push(Velocity { vx: 0 });
// keep it simple and loop only twice
for i in 0..2 {
println!("Turn {}", i);
for (i, b_outer) in bodies.iter().enumerate() {
println!("x:{}, y:{}, v:{}", b_outer.x, b_outer.y, velocities[i].vx);
let v = velocities.get_mut(i).unwrap();
let mut a = v.vx;
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, v.vx, a);
}
v.vx = a;
}
}
println!("{:?}", bodies);
}
Output:
[Body { x: 10, y: 10 }, Body { x: 20, y: 30 }]