Well, this is the perfect application for unstable thread::scoped() :
#![feature(scoped)] use std::thread::{self, JoinGuard}; // tunable const NUMTHREADS: i32 = 4; fn f(val: i32) -> i32 { // expensive computation let res = val + 1; res } fn main() { // choose an odd number of elements let orig: Vec<i32> = (1..14).collect(); let mut guards: Vec<JoinGuard<Vec<i32>>> = vec!(); // split into slices for chunk in orig.chunks(orig.len() / NUMTHREADS as usize) { let g = thread::scoped(move || chunk.iter().cloned().map(f).collect()); guards.push(g); }; // collect the results let mut result: Vec<i32> = Vec::with_capacity(orig.len()); for g in guards { result.extend(g.join().into_iter()); } println!("Flattened result: {:?}", result); }
It is unstable and is unlikely to stabilize in this form, because it has its inherent flaw (here you can find more here ). As far as I can see, simple_parallel is just an extension of this approach - it hides messing around with JoinGuards , and can also be used in stable Rust (maybe with some unsafe ty, I think). However, this is not recommended for general use, as its documents suggest.
Of course, you can use thread::spawn() , but then you will need to clone each piece so that it can move into each thread:
use std::thread::{self, JoinHandle}; // tunable const NUMTHREADS: i32 = 4; fn f(val: i32) -> i32 { // expensive computation let res = val + 1; res } fn main() { // choose an odd number of elements let orig: Vec<i32> = (1..14).collect(); let mut guards: Vec<JoinHandle<Vec<i32>>> = vec!(); // split into slices for chunk in orig.chunks(orig.len() / NUMTHREADS as usize) { let chunk = chunk.to_owned(); let g = thread::spawn(move || chunk.into_iter().map(f).collect()); guards.push(g); }; // collect the results let mut result: Vec<i32> = Vec::with_capacity(orig.len()); for g in guards { result.extend(g.join().unwrap().into_iter()); } println!("Flattened result: {:?}", result); }
source share