rustrayon

Summing successes inside of a Rayon Parallelized for loop in Rust


I was having trouble understanding the monty hall problem so I put together some Rust. I have a function which determines whether or not a participant wins a game show. If they win the game show then the function returns true, otherwise it returns false. I wanted to test for very large numbers so I added a Rayon Parallel Iterator to the for loop. The code follows:

fn gameshow(num_doors: usize, num_runs: usize, change: bool) -> usize {
    let mut won_games:usize = 0;
    let runs: Vec<Vec<bool>> = vec![stagehand(num_doors); num_runs]; // Creates common solution 
    if change {
        ParallelIterator::for_each(IntoParallelIterator::into_par_iter(runs), |game| {
            let winner: bool = run_game_no_change(game); // Each door is chosen randomly when tested
            if winner { won_games += 1;}
        });
    }
    else {
        ParallelIterator::for_each(IntoParallelIterator::into_par_iter(runs), |game| {
            let winner: bool = run_game_no_change(game); // Each door is chosen randomly when tested
            if winner { won_games += 1;}
        });     
    }
    won_games   
}

When compiling it throws a "Cannot assign to won_games, as it is a captured variable in a Fn closure" error.


Solution

  • You can't modify a captured external state in parallel, or you could have data races. Rayon is specifically designed to prevent this.

    Instead of for_each, use map to map each bool to 0 or 1, then take the sum of that:

    fn gameshow(num_doors: usize, num_runs: usize, change: bool) -> usize {
        let runs: Vec<Vec<bool>> = vec![stagehand(num_doors); num_runs]; // Creates common solution 
        let won_games = if change {
            runs.into_par_iter().map(|game| {
                let winner: bool = run_game_no_change(game); // Each door is chosen randomly when tested
                if winner { 1 } else { 0 }
            }).sum()
        } else {
            runs.into_par_iter().map(|game| {
                let winner: bool = run_game_no_change(game); // Each door is chosen randomly when tested
                if winner { 1 } else { 0 }
            }).sum()    
        };
    
        won_games   
    }