Trouble iterating correctly

Hello i want to iterate over an 2d ndarray filled with one vector for each field.

But i am having trouble making a useful iteration because of the nested vectors.

here is how i declared the ndarray:

  pub chunks_prey:Array2::<Vec<Entity>>,
  pub chunks_hunt:Array2::<Vec<Entity>>,

(there are hunter entities and prey entities, hunter follow prey. and prey runs away. similiar to boids)

And here is how i am trying to iterate over it

  pub fn update_entities_chunked(&mut self){
    //random spawn location for all enties that will die this frame
    let spawn_x = rand::thread_rng().gen_range(0..self.w-1) as f32;
    let spawn_y = rand::thread_rng().gen_range(0..self.h-1) as f32;

    //for each entity in the chunk
    /* HOW TO ITEREATE CORRECTLY HERE??*/
    self.chunks_prey.par_iter().for_each(|v|{ //in the nd array is a Vec<Entity>
      for e in v{  //i want to edit the entities
        //check against own chunk
        let base_chunk_x = e.pos_x as usize/self.chunk_size;
        let base_chunk_y = e.pos_y as usize/self.chunk_size;

        // get the closest other hunter entity. we want to run away from the closest one
        let others = &self.chunks_hunt[[base_chunk_x, base_chunk_y]];
        let other = e.closest(others, self.w, self.h);
        //TODO check other possible chunks //not important in this forum post

        //react to the closest other
        let dis = other.2; //other is a tuple containing the position and distance (other_x, other_y, distance)

        //if a hunter is too close we got eaten
        if dis < 1.0 {
          //respawn at a random entity
          e.set_pos(spawn_x, spawn_y); //error here
        } else if dis < e.view { //we see the hunter and run away
          e.move_away(other.0, other.1);
        }
        //random movement
        let step = 4.5;
        let rand_x = rand::thread_rng().gen_range(-step..step);
        let rand_y = rand::thread_rng().gen_range(-step..step);
        e.move_by(rand_x, rand_y);
        //make sure the entity doesn't walk offscreen
        e.set_pos(warp(e.pos_x, self.w), warp(e.pos_y, self.h));
      }
    });
  }

I feel like i am doing this completely wrong. I want to edit entity e by moving it around etc. so e should be a mut. however i don;t know how to tell this the compiler. Do i have to par_iter_mut the nd array too?

Here is the error i am getting.

error[E0596]: cannot borrow `*e` as mutable, as it is behind a `&` reference
   --> src/world.rs:221:11
    |
205 |       for e in v{
    |                - this iterator yields `&` references
...
221 |           e.set_pos(spawn_x, spawn_y);
    |           ^ `e` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*e` as mutable, as it is behind a `&` reference
   --> src/world.rs:223:11
    |
205 |       for e in v{
    |                - this iterator yields `&` references
...
223 |           e.move_away(other.0, other.1);
    |           ^ `e` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*e` as mutable, as it is behind a `&` reference
   --> src/world.rs:229:11
    |
205 |       for e in v{
    |                - this iterator yields `&` references
...
229 |           e.move_by(rand_x, rand_y);
    |           ^ `e` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*e` as mutable, as it is behind a `&` reference
   --> src/world.rs:237:9
    |
205 |       for e in v{
    |                - this iterator yields `&` references
...
237 |         e.move_by(rand_x, rand_y);
    |         ^ `e` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error[E0596]: cannot borrow `*e` as mutable, as it is behind a `&` reference
   --> src/world.rs:239:9
    |
205 |       for e in v{
    |                - this iterator yields `&` references
...
239 |         e.set_pos(warp(e.pos_x, self.w), warp(e.pos_y, self.h));
    |         ^ `e` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to 5 previous errors; 4 warnings emitted

For more information about this error, try `rustc --explain E0596`.
error: could not compile `pixels`

To learn more, run the command again with --verbose.

Yes. With only a few exceptions, accessing through a non-mut reference is read-only, no matter how deep you go after that.

1 Like

changing the ndarray iteration to par_iter_mut doesnt change the error.

i think the problem is the for each. Since there is just one vector in each 2d array element it makes no sense to do a for each. Also does the for each prevent me from mutating the entity e?

how can i 'enter' the vector that is in the 2darray?

i was thinking about using an 3d array instead but then i would have to set the size at compile time. So i have to have a vec inside a 2darray.

That's strange, since it should change the types of the v and e variables in your code. Can you paste the exact error you get after changing par_iter to par_iter_mut?

ok the code i have now is:

//for each entity in the chunk
self.chunks_prey.par_iter_mut().for_each(|v| {
  

  for e in v{

the error is this:

error[E0502]: cannot borrow `self` as immutable because it is also borrowed as mutable
   --> src/world.rs:205:46
    |
205 |     self.chunks_prey.par_iter_mut().for_each(|v| {
    |     ----------------                -------- ^^^ immutable borrow occurs here
    |     |                               |
    |     |                               mutable borrow later used by call
    |     mutable borrow occurs here
...
210 |         let base_chunk_x = e.pos_x as usize/self.chunk_size;
    |                                             ---- second borrow occurs due to use of `self` in closure

error: aborting due to previous error; 5 warnings emitted

For more information about this error, try `rustc --explain E0502`.
error: could not compile `pixels`

This looks like a different error to me :confused:

Anyway, currently closures don't capture disjoint fields, meaning that if you use self.chunk_size the closure will capture self/&self/&mut self. The problem is that you're mutably borrowing self.chunk_prey outside the for_each closure, however the closure also borrows the whole self, and this creates a conflict.

The solution is to create a reference to the fields you need outside the closure, and then use them inside. This way you don't ever directly use self inside the closure and the borrow checker is happy.

For example:

let chunk_size = self.chunk_size; // This is probably `Copy` so it's more ergonomic to not borrow
let chunks_hunt = &self.chunks_hunt; // This is probably not `Copy` so you have to borrow
// Make other bindings here
self.chunks_prey.par_iter_mut().for_each(|v| {
    // Remove all `self.` from here
}
1 Like