Rayon par_iter not working on Vec<struct>

I tried using par_iter on a vector filled with structs:

use rayon::prelude::*;

struct Test{
  int:i32,
  f:f32
}

fn main() {
  //create a vector with type struct
  let mut arr = Vec::<Test>::new();
  //fill the vector
  for i in 0..10{
    arr.push(Test{int:i as i32, f:(i as f32 + 0.123)});
  }

  //print it sequentially: this works
  for t in arr.iter(){
    println!("{}, {}", t.int, t.f);
  }

  //print it parallel: this errors out
  for t in arr.par_iter(){
    println!("{}, {}", t.int, t.f);
  }
}

But i am getting this error:

error[E0277]: `rayon::slice::Iter<'_, Test>` is not an iterator
  --> src/main.rs:22:12
   |
22 |   for t in arr.par_iter(){
   |            ^^^^^^^^^^^^^^ `rayon::slice::Iter<'_, Test>` is not an iterator
   |
   = help: the trait `Iterator` is not implemented for `rayon::slice::Iter<'_, Test>`
   = note: required by `std::iter::IntoIterator::into_iter`

Is there a reason i am not aware of why this shouldn't work?

Being new to rust i am getting bombarded with errors that only make sense after a long while of thinking about them. Is this such a case?

If i only need to implement some sort of iterator. How would i go about that? I mean i am using nothing fancy. Just a normal Vec. Should i make my own container that is basically just a Vec and then add the iterator to it? How would i even go about this?

Thanks or helping confused beginners out.

The for syntax in Rust only works with “ordinary” sequential iterators. The abstraction of parallel iteration provided by rayon does thus not work with for syntax. The way you can write something similar to a for loop is by using the for_each method. Of course, printing to stdout is not an operation for which parallelization makes much sense: The elements are potentially printed in a weird order and printing would have to coordinate through a global lock on stdout anyways. Nonetheless, using for_each, you can make your program compile.

Your original code,

for t in arr.par_iter() {
    println!("{}, {}", t.int, t.f);
}

now becomes

arr.par_iter().for_each(|t| {
    println!("{}, {}", t.int, t.f);
});

The |t| { ... } syntax here is defining a closure. Such a for_each call is a bit more limited than an ordinary for-loop, in that it doesn’t support continue or break or early returns from the containing function. Compared to the for_each method that ordinary iterators offer as well, in the case of rayon’s parallel iterators there are further restrictions around thread-safety that you might encounter, so feel free to ask more questions in case you’re trying to solve some more practical example next and are having trouble getting that to work.

5 Likes

Thank you for you Answer!

i kept working on it and now i am getting this error:

error[E0277]: `RefCell<Vec<Entity>>` cannot be shared between threads safely
  --> src/world.rs:69:43
   |
69 |       self.prey.borrow_mut().par_iter_mut().for_each(|e|{
   |  ___________________________________________^^^^^^^^_-
   | |                                           |
   | |                                           `RefCell<Vec<Entity>>` cannot be shared between threads safely
70 | |       let other = e.closest(&self.hunters.borrow());
71 | |       let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
72 | |       //if a hunter is too close we got eaten
...  |
86 | |       e.set_pos(warp(e.pos_x, self.w), warp(e.pos_y, self.h));
87 | |     });
   | |_____- within this `[closure@src/world.rs:69:52: 87:6]`
   |
   = help: within `[closure@src/world.rs:69:52: 87:6]`, the trait `Sync` is not implemented for `RefCell<Vec<Entity>>`
   = note: required because it appears within the type `World`
   = note: required because it appears within the type `&mut World`
   = note: required because it appears within the type `&&mut World`
   = note: required because it appears within the type `[closure@src/world.rs:69:52: 87:6]`

Does this mean that i can never use it with RefCall?

I know RefCall is a dirty hack, but i just wanted to see it working for now before i would have to refactor my code again.

Ah, yes that’s the “restrictions around thread-safety that you might encounter”. The multi-threaded analogue of RefCell would be to use a Mutex instead. Or possibly an RwLock, for that matter. Whether any of these make sense for you is hard to answer without seeing the code. There might be other solutions, too. And one thing to keep in mind around Mutex is that too much locking defeats the point of parallelization. I guess what I’m saying is: feel free to provide a more complete code example xD

ok i can post the whole thing. But its my first Rust project i am certain its wrong in more than one way. There def some warnings due to unused code etc. But i made sure the way i post it here it compiles and runs on my machine (linux):

main.rs:

use std::time::Instant;

mod entity;
mod world;
mod screen;

//define window dimensions
const WIDTH:usize = 1600;
const HEIGHT:usize = 900;
const FPS:u64 = 21000;

//define pixel entity
const MAX_ENTITY:usize = 20000;

fn main() {

  //generate the World
  let mut world = world::World::new(WIDTH, HEIGHT, MAX_ENTITY);

  let mut screen = screen::Screen::new(WIDTH, HEIGHT, FPS);
  let mut frame_counter:f32 =0.0;
  let mut timer = Instant::now();
  //main loop
  while screen.is_open(){

    //update prey
    //update hunters
    world.update_entities();

    screen.write_into_buffer(&world.prey.borrow());
    screen.write_into_buffer(&world.hunters.borrow());

    //draw pixels to screen
    screen.show();
    // prepare for next frame with black background
    screen.clear();

    //calc fps
    frame_counter+=1.0;
    if frame_counter % 60.0 == 0.0{
      println!("fps: {}, prey: {}, hunters: {}"
        , 1000.0*frame_counter/timer.elapsed().as_millis()as f32
        , world.prey.borrow().len()
        , world.hunters.borrow().len()
        );
        let timer = Instant::now();
    }
  }
}

world.rs:

use crate::entity::Entity;
use rayon::prelude::*;
use std::cell::RefCell;
//use crossbeam_utils::thread;

use rand::Rng;

/*
  functions that are not tied to the struct
  but are useful in the context of level
*/

const RED:u32   = 0xff_00_00;
const GREEN:u32 = 0x00_ff_00;
const BLACK:u32 = 0x00_00_00;

//return the distance between two points
pub fn distance(x0:f32, x1:f32, y0:f32, y1:f32)->f32{
  return ((x0-x1).powf(2.0) + (y0-y1).powf(2.0)).sqrt();
}

//return the square distance between two points
pub fn distance_sq(x0:f32, x1:f32, y0:f32, y1:f32)->f32{
  return (x0-x1).powf(2.0) + (y0-y1).powf(2.0);
}

/* leaving on one side of the screen warps to the other */
pub fn warp(xx:f32, wall:usize)->f32{
    let mut x = xx;
    while x> wall as f32{ x-= wall as f32;}
    while x < 0.0{ x+= wall as f32;}
    if x as usize == wall { x-=1.0;}
    //println!("{}, {}, {}", xx, x, wall);

    return x;
}

/*
  struct
*/

pub struct World{
  //window drawable area buffer
  pub w:usize,
  pub h:usize,

  //entity container
  pub hunters:RefCell<Vec<Entity>>,
  pub prey:RefCell<Vec<Entity>>,
  pub chunk:usize
}

impl World{

  pub fn new(width:usize, height:usize, max_entity:usize)->World{
    let h = max_entity/100;
    return World{
      chunk:32 as usize,
      prey:RefCell::new(vec![Entity::prey(); max_entity-h]),
      hunters:RefCell::new(vec![Entity::hunter(); h]),

      w:width as usize, 
      h:height as usize, 
    };
  }

  pub fn update_entities(&mut self){
    //prey runs away
    self.prey.borrow_mut().iter_mut().for_each(|e|{
      let other = e.closest(&self.hunters.borrow());
      let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
      //if a hunter is too close we got eaten
      if dis < 1.0{
        //teleport away eg respawn somewhere else
        let rand_x = rand::thread_rng().gen_range(0.0..self.w as f32);
        let rand_y = rand::thread_rng().gen_range(0.0..self.h as f32);
        e.set_pos(rand_x , rand_y);
      }else if  dis < e.view {
        e.move_away(other.0, other.1);
      }
      //random movement
      let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
      let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
      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));
    });

    //hunters follow
    for e in self.hunters.borrow_mut().iter_mut(){
      let other = e.closest(&self.prey.borrow());
      let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
      if dis < e.view {
        e.move_to(other.0, other.1);
      }

      //random movement
      let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
      let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
      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));
    }
  }
}

entity.rs:

//colors
const RED:u32 = 0xff_00_00;
const GREEN:u32 = 0x00_ff_00;
const BLACK:u32 = 0x00_00_00;
use crate::world;

//one entity
#[derive(Clone,Debug)]
pub struct Entity {
  pub pos_x:f32,
  pub pos_y:f32,
  pub color:u32,
  pub speed:f32,
  pub hp:i32,
  pub view:f32,
}

//constructor
impl Entity {
  pub fn prey() -> Entity {
    let e = Entity {pos_x:100.0, pos_y:100.0, color:GREEN, speed:2.0, hp:100, view:20.0};
    return e;
  }



  pub fn hunter()->Entity{
    Entity {pos_x:200.0, pos_y:100.0, color:RED, speed:1.0, hp:100, view:22.0}
  }



  pub fn move_by(&mut self, u:f32,v:f32){

    let mut x = u;
    let mut y = v;

    //calc the actual distance with pythagoras
    let distance = (x.powf(2.0) + y.powf(2.0)).sqrt();
    //scale down if further away than the speed
    if distance > self.speed{
      let scale = self.speed/distance;
      x = x*scale;
      y = y*scale;
    }

    self.pos_x += x;
    self.pos_y += y;
  }



  pub fn move_to(&mut self,x:f32,y:f32){
    self.move_by(x-self.pos_x, y-self.pos_y);
  }



  pub fn move_away(&mut self,x:f32,y:f32){
    self.move_by(self.pos_x-x, self.pos_y-y);
  }

  pub fn set_pos(&mut self, x:f32, y:f32){
    self.pos_x = x;
    self.pos_y = y;
  }
/*
  //which of those entities is the closest to your own position
  pub fn closest(&self, others:&Vec<Entity>) -> (f32, f32){

    return thread::scope(|s| {
      let pt = s.spawn(|_|{
        return self.closest_par(others,0,  others.len()/2);
      });
      let bt = s.spawn(|_|{
        return self.closest_par(others, others.len()/2, others.len());
      });

      let p = pt.join().unwrap();
      let b = bt.join().unwrap();

      if p.2 < b.2{
        return (p.0,p.1);
      }
        return (b.0,b.1);
    }).unwrap();
  //return (0.0 , 0.0);
  }
*/
  pub fn closest(&self, others:&Vec<Entity>) -> (f32, f32){
    let mut closest:f32 = 10000000.0;//f32.MAX;
    let mut x:f32 = 0.0;
    let mut y:f32 = 0.0;
    for other in others.iter(){
      let dis = world::distance_sq(other.pos_x, self.pos_x, other.pos_y, self.pos_y);
      if closest > dis{
        closest = dis;
        x = other.pos_x;
        y = other.pos_y;
      }
    }
    return (x,y);
  }
}

screen.rs:

use minifb::{Key, Scale, Window, WindowOptions};
use std::time::Instant;
use crate::entity::Entity;
/*
deals with everything that hass to do with rendering
*/

pub struct Screen{
  h:usize,
  w:usize,
  buffer:Vec<u32>,
  win:minifb::Window,
  fps:u64
}


impl Screen{

  pub fn new(width:usize, height:usize, fps:u64)->Screen{
    let mut s = Screen{
      //make a window
      win:Window::new(
        "Pixels!",
        width,
        height,
        WindowOptions {
          scale: Scale::X1, ..WindowOptions::default()
        },
      ).unwrap(),
      fps:fps,
      h:height,
      w:width,
      buffer:vec![0; width * height]
    };
    //limit fps
    s.win.limit_update_rate(Some(std::time::Duration::from_micros(1000000/fps)));
    return s;
  }

  pub fn show(&mut self){
    self.win.update_with_buffer(&self.buffer, self.w, self.h).unwrap();
  }

  pub fn clear(&mut self){
    self.buffer = vec![0; self.w * self.h];
  }

  pub fn is_open(&self)->bool{
    self.win.is_open() && !self.win.is_key_down(Key::Escape)
  }

  pub fn cartesian_to_array(&self, x:f32, y:f32) -> usize{
    let u = x;
    let v = y;

    let r = (u.floor() as usize + ((v.floor() as usize)*self.w));
    //println!("{} {} {}", u, v, r);
    return r;
  }


  pub fn write_into_buffer(&mut self, entities:&Vec<Entity>){

    //put pixel pos into buffer
    for e in entities.iter(){
      let index = self.cartesian_to_array(e.pos_x, e.pos_y);
      if index > self.h*self.w{
        println!("{}, {}, {}", index, e.pos_x, e.pos_y);
      }
      self.buffer[index] = e.color;
    }
  }
}

dependencies in Cargo.toml:

[dependencies]
minifb = "0.19.1"
rand = "0.8.0"
chrono = "0.4.19"
rayon = "1.5.0"
crossbeam-utils = "0.8"

Well, in this particular case, avoiding accessing the RefCell in the loop is possible, e.g.:

pub fn update_entities(&mut self) {
    let Self {
        w,
        h,
        ref hunters,
        ref prey,
        ..
    } = *self;
    {
        let hunters = hunters.borrow(); // gets dropped at end of block
        let hunters = &*hunters;
        //prey runs away
        self.prey.borrow_mut().par_iter_mut().for_each(|e| {
            let other = e.closest(hunters);
            let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
            //if a hunter is too close we got eaten
            if dis < 1.0 {
                //teleport away eg respawn somewhere else
                let rand_x = rand::thread_rng().gen_range(0.0..w as f32);
                let rand_y = rand::thread_rng().gen_range(0.0..h as f32);
                e.set_pos(rand_x, rand_y);
            } else if dis < e.view {
                e.move_away(other.0, other.1);
            }
            //random movement
            let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
            let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
            e.move_by(rand_x, rand_y);
            //make sure the entity doesn't walk offscreen
            e.set_pos(warp(e.pos_x, w), warp(e.pos_y, h));
        });
    }

    {
        let prey = prey.borrow();
        let prey = &*prey;
        //hunters follow
        
        // I don’t know if you want parallel iteration here, too
        // but for demonstration’s sake, here we go:
        self.hunters.borrow_mut().par_iter_mut().for_each(|e| {
            let other = e.closest(prey);
            let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
            if dis < e.view {
                e.move_to(other.0, other.1);
            }

            //random movement
            let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
            let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
            e.move_by(rand_x, rand_y);
            //make sure the entity doesn't walk offscreen
            e.set_pos(warp(e.pos_x, w), warp(e.pos_y, h));
        });
    }
}

One has to wonder though if it isn’t possible to avoid the use of RefCell alltogether (haven’t read through all the code, so I don’t really know why it’s there in the first place). Anyways, I’m going offline now, hope this helped ^^

1 Like

Wow that works!
Its so fast.

OK But how do i understand what happens in the code?
can you explain what your thought processes were and is this the correct way to do it or did you needed to hack something together to fit my beginner code?

why this line?
let Self {w, h,ref hunters, ref prey,chunk} = *self;

and this one?

let hunters = &*hunters;

isn't the & and the * cancelling each other out?

That's a "reborrow", temporarily morphing the unique, non-shareable mut hunters into a shareable read-only hunters.

1 Like

Well, yeah it is a workaround to the fact that there are RefCells in World, but you only read them in this particular loop. It’s also a workaround to the fact that closures currently want to have access to all of self even if you only use certain fields inside of them, something that IIRC is going probably/possibly to change in the future as well. As such all the necessary fields need to be extracted before the loop.

Furthermore, since RefCells cannot be referenced in a closure that is supposed to be executed in another thread, the call to hunters.borrow() needs to happen before the loop, too. And that call returns a std::cell::Ref object that doesn’t support being shared between threads either, so the let hunters = &*hunters; line is what dereferences the Ref<'_, Vec<Entity>> into a &Vec<Entity> which then can be used in the closure.

The line

let Self {w, h, ref hunters, ref prey, ..} = *self;

is equivalent to

let w = self.w; // can be accessed by-value
let h = self.h; // since `usize` can be copied

let hunters = &self.hunters;
let prey = &self.prey;
1 Like

Removing the RefCells is fairly straightforward.


pub struct World {
    //window drawable area buffer
    pub w: usize,
    pub h: usize,

    //entity container
    pub hunters: Vec<Entity>,
    pub prey: Vec<Entity>,
    pub chunk: usize,
}

impl World {
    pub fn new(width: usize, height: usize, max_entity: usize) -> World {
        let h = max_entity / 100;
        return World {
            chunk: 32 as usize,
            prey: vec![Entity::prey(); max_entity - h],
            hunters: vec![Entity::hunter(); h],

            w: width as usize,
            h: height as usize,
        };
    }

    pub fn update_entities(&mut self) {
        let Self { w, h, hunters, prey, ..} = self;
        //prey runs away
        prey.par_iter_mut().for_each(|e| {
            let other = e.closest(hunters);
            let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
            //if a hunter is too close we got eaten
            if dis < 1.0 {
                //teleport away eg respawn somewhere else
                let rand_x = rand::thread_rng().gen_range(0.0..*w as f32);
                let rand_y = rand::thread_rng().gen_range(0.0..*h as f32);
                e.set_pos(rand_x, rand_y);
            } else if dis < e.view {
                e.move_away(other.0, other.1);
            }
            //random movement
            let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
            let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
            e.move_by(rand_x, rand_y);
            //make sure the entity doesn't walk offscreen
            e.set_pos(warp(e.pos_x, *w), warp(e.pos_y, *h));
        });

        //hunters follow
        hunters.par_iter_mut().for_each(|e| {
            let other = e.closest(prey);
            let dis = distance(other.0, e.pos_x, other.1, e.pos_y);
            if dis < e.view {
                e.move_to(other.0, other.1);
            }

            //random movement
            let rand_x = rand::thread_rng().gen_range(-1.0..1.0);
            let rand_y = rand::thread_rng().gen_range(-1.0..1.0);
            e.move_by(rand_x, rand_y);
            //make sure the entity doesn't walk offscreen
            e.set_pos(warp(e.pos_x, *w), warp(e.pos_y, *h));
        });
    }
}

Still needing to split up the &mut World into references to the individual fields because otherwise the closure is unhappy about not having access to all of self (since prey/hunters is already borrowed mutably for the iteration).

Furthermore I can recomment using clippy. It will tell you about things like e.g. unneeded return statements, improvements like x = x * scale can be written as x *= scale or suggest using &[Entity] instead of &Vec<Entity> in function parameters.

how would i do that?
Having an array outside of world? Or can i keep it in world?
I struggled alot with the &mut self vs &self. Because i can't change and read from the same 'self'. What would be the rustacian way to solve this? Rust also forced me to keep the self in arguments. Maybe i could move that function out of the world struct ... but wouldn't a function outside of world with the arguments (&mut world.prey, &world.hunters) for example give me the same trouble?

I kept it inside world because i wanted to divide the world into smaller chunks later on so i wouldn't have to do n**2 distance comparisons. My plan was to only compare distance to all entities in the same chunk (32x32 pixels) and all neighboring ones. That would cut down the double for loop over all entities to just small local subsets of entities.

That is why i wanted to keep the entities inside world. Each chunk is basically a smaller world. But it was easier to implement it as one big container first.

is there any way to do this without unsafe code?

I'm not quite sure I understand the question. The code in my previous comment does just that, splitting up the &mut self paramer into mutable references to all the relevant fields with the first let statement.

The "still needing to" refers to my previous code that demonstrated how to work with the RefCells where splitting up the reference happened as well actually, nevermind I only created shared/immutable references to the fields I'm that code. (Though I suppose with RefCells, only immutable references to the individual fields would've been were necessary).

I assumed that your comment meant that the code wouldn't be complete and the split was missing. I implemented as suggested and it works.

Am i right to assume that you used shadowing to generate a &mut and & from the same data fields?

I see, I might have expressed myself unfortunately in this regard.

What part of the code exactly are you referring to? Shadowing is only about naming things and does have no influence on the borrow checker.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.