Use Box<dyn > with rayon crate

Hi,
In the following code, I would make evaluate_solution() in main() function run in parallel mode using rayon crate. But, the code doesn't work. How can I do that ?

extern crate rayon;
use rayon::prelude::*;

 pub trait Mytrait {
       fn testfn(&mut self, solution : &Vec<f64>)-> f64 {
            solution.iter().fold(0.0f64, |sum, x| sum +x)
      }
}

pub struct Solution{
      value: f64,
      data : Vec<f64>,
      problem : Box<dyn Mytrait + 'static>,
}

impl Solution {
      pub fn new(gens : Vec<f64>, problem :Box<dyn Mytrait+ 'static>)->Solution {
           Solution {
                 values: 0.0f64,
                 data : gens,
                problem : problem,
      }
 }

  pub fn evaluate_solution(&mut self){
          self.values=self.problem.testfn(&self.data);
       }
 }

 #[derive(Debug, Clone)]
 pub struct Problem{
        state : bool,
 }

 impl Mytrait for Problem {
         fn testfn(&mut self, solution : &Vec<f64>) -> f64 {
                    self.state = true;
                     let prod = solution.iter().fold(0.0f64, |sum, x| sum * x);
                      prod 
        }
 }

 fn main() {

         let mut p = Problem{
                state : false,
         };

      //Initialization 
       let mut list = initialize(&mut p);

        //Parallel evaluation 
         list.par_iter_mut().for_each(|s| 
                       s.evaluate_solution()
        );

   for g in list.iter(){
           println!("value: {}", g.values);
      }   
}

fn initialize<T: Mytrait + Clone + 'static>(problem : &mut T)->Vec<Solution>{

         //initialize default data
         let mut data =Vec::new();
          for j in 0..100{
                   data.push(j as f64);
          }

        let mut list = Vec::new();
        for _i in 0..10{

                  //make new copy of problem
                 let nwp = problem.clone();
    
                 let g = Solution::new(data.clone(), Box::new(nwp));
                 list.push(g);
       };
     list
}

When using dyn Traits, the compiler will no longer see other traits that your original type might have implementing, such as the "I'm thread-safe" marker traits (Send expresses that exclusive access (e.g., owned or &mut) can safely cross thread boundaries, and Sync expresses the same but for shared access (e.g., &), i.e., that something can be manipulated from within multiple threads / in parallel safely — the default for most types, such as bool, unless unsynchronized shared mutability is involved, such as with Cell, RefCell, or Rc).

In your case, you do have a Send + Sync type, so just make it so you don't make the compiler "forget about it":

  • either by using Box<dyn MyTrait + Send + Sync + 'static>, to express that these very instances, on top of complying to the Mytrait contract / protocol / API, they also have to be thread-safe;

  • or by adding : Send + Sync to the definition of Mytrait, to express that thread-safety is also part of the requirements to comply to Mytrait


Aside: rather than tabulating your code, try to use triple-backquotes for your code snippets, like this:

```
extern crate rayon;
use rayon::prelude::*;

…

    list
}
```
  • This way your code snippet gets to feature syntax highlighting :slight_smile:

That is, do either of the two following changes:

  extern crate rayon;
  use rayon::prelude::*;
  
- pub trait Mytrait {
+ pub trait MyTrait : Send + Sync {
      fn testfn(&mut self, solution: &Vec<f64>) -> f64 {
          solution.iter().fold(0.0f64, |sum, x| sum + x)
      }
  }
  
  pub struct Solution {
      value: f64,
      data: Vec<f64>,
-     problem: Box<dyn Mytrait + 'static>,
+     problem: Box<dyn Mytrait + Send + Sync + 'static>,
  }
  
  impl Solution {
      pub fn new(gens: Vec<f64>, problem: Box<dyn Mytrait + 'static>) -> Solution {
          Solution {
              values: 0.0f64,
              data: gens,
              problem: problem,
          }
      }
  
      pub fn evaluate_solution(&mut self) {
          self.values = self.problem.testfn(&self.data);
      }
  }
  
  #[derive(Debug, Clone)]
  pub struct Problem {
      state: bool,
  }
  
  impl Mytrait for Problem {
      fn testfn(&mut self, solution: &Vec<f64>) -> f64 {
          self.state = true;
          let prod = solution.iter().fold(0.0f64, |sum, x| sum * x);
          prod
      }
  }
  
  fn main() {
      let mut p = Problem { state: false };
  
      //Initialization
      let mut list = initialize(&mut p);
  
      //Parallel evaluation
      list.par_iter_mut().for_each(|s| s.evaluate_solution());
  
      for g in list.iter() {
          println!("value: {}", g.values);
      }
  }
  
  fn initialize<T: Mytrait + Clone + 'static>(problem: &mut T) -> Vec<Solution> {
      //initialize default data
      let mut data = Vec::new();
      for j in 0..100 {
          data.push(j as f64);
      }
  
      let mut list = Vec::new();
      for _i in 0..10 {
          //make new copy of problem
          let nwp = problem.clone();
  
          let g = Solution::new(data.clone(), Box::new(nwp));
          list.push(g);
      }
      list
  }
  • Remark: when you think about it, 'static is also such a marker property that you already had to express sometimes, and in that regard you can also embed it inside the trait definition to avoid having to require it elsewhere:

    trait Mytrait : Send + Sync + 'static {
    

    So that impl Mytrait, T : Mytrait and Box<dyn Mytrait> all Just Work™ everywhere.

4 Likes

Finally it works ... it is so helpful. Thank you so much for all.

1 Like

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.