4

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris
  • 480
  • 1
  • 5
  • 11
  • 2
    In your case, I think you could try `Vec>`, because `Cell` have "inner mutability". – Matthieu M. Apr 02 '15 at 13:40
  • 1
    Note: I ignored the "obvious" answer here, create a clone of `bodies`, and modify the clone while you iterate on `bodies`. It has the obvious disadvantage of requiring twice as much memory. – Matthieu M. Apr 02 '15 at 13:46
  • @MatthieuM. see my answer - I think modifying the array in-place during iteration is *incorrect* for this problem. – Shepmaster Apr 02 '15 at 13:59
  • @Shepmaster: Ah, I did not think about challenging the logic :) – Matthieu M. Apr 02 '15 at 14:27

3 Answers3

4

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 } }]
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    Concerning the logic problem, I would agree if b.v is changed during the inner loop (b_inner). But that's not the case. v of the inner body (b_inner) is not changed during the inner loop. b.v is updated after the inner loop has finished. Then b_outer advances to the next body. – Chris May 25 '15 at 10:28
  • 1
    I came up with another solution in the meantime (though I don't know if this is more elegant): I split the structure, one that is the base for the calculation (input) and another that gathers the results (output). After the calculation finished, the input structure is updated with results from the output structure and the next turn of the calculation starts. – Chris May 25 '15 at 10:34
  • I wonder what a solution using Cell would look like. I'm trying to rewrite his first code using Cells but I keep stumbling upon the original problem. – Ricardo Pieper Dec 02 '17 at 18:47
  • @Shepmaster That was fast, thanks! I figured you'd have to derive `Copy, Clone` in order to use `get`. I'm writing an answer to share my findings as well, though I'm only learning Rust. – Ricardo Pieper Dec 02 '17 at 20:33
3

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ricardo Pieper
  • 2,613
  • 3
  • 26
  • 40
  • 2
    Seems reasonable to me. This has the benefit of being very explicit about exactly what is able to be changed during the iteration. – Shepmaster Dec 03 '17 at 15:09
1

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 }]
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Chris
  • 480
  • 1
  • 5
  • 11
  • 1
    Just want to point out that these changes may not reflect the original intent. Of course, the end result is the same in both cases, but the stdout is slightly different. – Ricardo Pieper Dec 03 '17 at 18:59