I'm trying to use Rayon to initiate a series of top level threads to call a simulation function recursively. The code works when using channel send & receive so it is multi-threading compatible, but it fails to compile with par_iter().

fn simulate(initial_board: &Board, player: Player, level: u32, start: bool) -> Option<AMove> {
...

    #[inline(always)]
    fn evaluate_move(previous: Option<AMove>, new_move: &AMove, new_score: i32, player: Player) -> AMove {
    ...
    }

... 

let accumlator = |previous: Option<AMove>, a_move: &Option<AMove>| if let Some(AMove { board: ref a_board, .. }) = *a_move {
        ...
    } else {
        previous
    };

    if start && !winning {
        the_move = moves.par_iter().fold(the_move, accumlator);
    } else {
        the_move = moves.iter().fold(the_move, accumlator);
    }

    the_move
}

I get a compiler error on the line with par_iter() and I'm lost on how to fix these.

error[E0277]: the trait bound `std::option::Option<AMove>: std::ops::Fn<()>` is not satisfied
   --> src/main.rs:271:37    
    |
271 |         the_move = moves.par_iter().fold(the_move, accumlator);
    |                                     ^^^^ the trait `std::ops::Fn<()>` is not implemented for `std::option::Option<AMove>`

error[E0277]: the trait bound `std::option::Option<AMove>: std::ops::FnOnce<()>` is not satisfied
   --> src/main.rs:271:37
    |
271 |         the_move = moves.par_iter().fold(the_move, accumlator);
    |                                     ^^^^ the trait `std::ops::FnOnce<()>` is not implemented for `std::option::Option<AMove>`

error[E0308]: mismatched types
   --> src/main.rs:271:20
    |
271 |         the_move = moves.par_iter().fold(the_move, accumlator);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        expected enum `std::option::Option`, found struct `rayon::iter::Fold`
    |
    = note: expected type `std::option::Option<_>`
               found type `rayon::iter::Fold<rayon::slice::Iter<'_, std::option::Option<AMove>>, std::option::Option<_>, [closure@src/main.rs:224:22: 240:6 winning:_, level:_, player:_]>`
share|improve this question
    
Welcome to Stack Overflow! Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Otherwise, your question is likely to be closed as off-topic. – Shepmaster Jun 12 at 19:35
    
My expectation is that par_iter() "behaves" the same as "iter()" or an advise to achieve similar behavior. – Serge Robyns Jun 12 at 20:49
5  
@SergeRobyns Please read How to Ask and Minimal, Complete, and Verifiable example. – Stargateur Jun 12 at 21:38

Rayon's fold wants a function which produces an identity element, rather than a single base element. In your case, if AMove is Copy + Send + Sync, simply doing moves.par_iter().fold(|| the_move, accumlator) should be good enough.

If AMove is not Copy, use || the_move.clone().

Rayon may want to produce multiple identity elements to do different chunks in parallel and only merge the results at the end, that's why it needs be able to produce as many identity elements as it needs.

Check fold's signature. Furthermore, if your accumulator returns the same type as the identity element, you probably want to use reduce instead (see linked docs for differences).

A different problem is that you can't use recursive closures the way you're trying to (Rust just can't do that). Use a named function for your accumulator instead.

share|improve this answer
    
It's getting better. AMove has copy and clone for sure: #[derive(Debug, Copy, Clone)] struct AMove { column: usize, board: Board, score: i32, cumulative_score: i32, cumulative_moves: i32, } Nevertheless I tried to use your version of par_iter and I'm now getting a different kind of error. The doc isn't helping much with my situation. The error is hinting that I need to "fold" the results of fold, as much as the doc states the same, however the doc is using a sum function as an example and I don't need it. – Serge Robyns Jun 14 at 13:30
    
the_move = moves.par_iter().fold(|| the_move, accumlator); expected enum std::option::Option, found struct rayon::iter::Fold – Serge Robyns Jun 14 at 13:41
    
Did you try using reduce instead? Fold will give you another iterator of results, that you can then merge manually somehow (eg with reduce). If the accumulator is associative and returns AMove, then you should definitely use reduce rather than fold. – cristicbz Jun 14 at 22:31
    
Based on all your inputs and an example in the Rayon demo, I'm now using par_iter(...).fold(| | ..).redeuce(| | ...) and it works. – Serge Robyns Jun 16 at 20:01

The solution lies in using reduce on the resulting fold. Thanks for hinting me. I finally found an example in the Rayon demo that paved the way to the solution for my case. Here is the the way for future references.

moves.par_iter()
     .fold(|| None, accumlator)
     .reduce(|| None, |previous: Option<AMove>, a_move: Option<AMove>| {
        if let Some(a_move) = a_move {
            Some(select_move(&previous, &a_move, player))
        } else {
            previous
        }
    })

The most puzzling error was the initial one that came because I was using fold(a,b) instead of fold(|| a,b), which complained about Fn and FnOnce and left me clueless.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.