Search code examples
arraysrustmutablerayon

I am unable to update to different mutable arrays while processing a big array using par_iter()


A large array of structs to be processed and two different mutable arrays should hold the values.

I used par_iter and got the following error:

fn par_calculate_depreciation_for_assets(assets_to_depreciate: &Vec<Depreciation>) -> Result<(), Ferror> {
    let mut acc_dep = 0;
    let mut errors: Vec<String> = vec![];
    let mut assets_record : Vec<AssetRecord> = Vec::new();

    assets_to_depreciate.par_iter().for_each( |item| {
        
        let result = calculate_monthly_depreciation(item);
        
        if result.is_err()
            {
                errors.push(item.asset_code.clone());
         
            }
        else
            {
                let (asset, depreciated_asset) = result.unwrap();
                build_csv_struct(item, &mut assets_record, asset);
            } 
    });
    
    append_to_csv(assets_record);
    dbg!("Errors {:?} unprocessed", errors);
    Ok(())
}
error[E0596]: cannot borrow `errors` as mutable, as it is a captured variable in a `Fn` closure
  --> src/services/csv_services/dep_for_all_assets_in_csv_copy.rs:69:17
   |
69 |                 errors.push(item.asset_code.clone());
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable

Solution

  • The error you currently see is that errors is mutably captured by your closure implementing the Fn trait you pass to the for_each call on your parallel iterator, which Rust does not allow.

    There is a more fundamental problem with your current implementation, though. There is a race condition. What happens if two threads simultaneously try to add an element to errors? You must make sure that access to errors is mutually exclusive. I can really recommend reading the shared-state concurrency section of the Rust book: https://doc.rust-lang.org/book/ch16-03-shared-state.html.

    Here a minimal example that fixes your error and the race condition, by wrapping errors in a Mutex:

    use rayon::prelude::*;
    
    use std::sync::Mutex;
    
    fn par_calculate_depreciation_for_assets(assets_to_depreciate: &Vec<u8>) -> Vec<String> {
        let errors: Mutex<Vec<String>> = Mutex::new(vec![]);
    
        assets_to_depreciate.par_iter().for_each(|item| {
            let mut errors = errors.lock().unwrap();
            errors.push(item.to_string());
        });
    
        errors.into_inner().unwrap()
    }
    
    fn main() {
        let errors = par_calculate_depreciation_for_assets(&vec![0, 1, 2, 3]);
    
        assert_eq!(
            errors,
            vec![
                "0".to_owned(),
                "1".to_owned(),
                "2".to_owned(),
                "3".to_owned()
            ]
        )
    }
    

    Playground.