2
votes

I'm trying to spawn several new threads, where each thread gets its own copy of some state. What I want is this:

use std::thread;

fn main() {
    let data = vec![42; 10];
    let more_data = "Important data".to_string();
    for _ in 1..=4 {
        thread::spawn(|| foo(data.clone(), more_data.clone()));
    }
}

fn foo(_data: Vec<u64>, _more_data: String) {}

This does not compile, because the clone is done on the new thread, which might outlive the main thread. Fair enough, but there seems to be no good way to tell Rust to do the clone on the main thread, and then move ownership to the new thread. Whenever I face this problem, I end up doing this:

use std::thread;

fn main() {
    let data = vec![42; 10];
    let more_data = "Important data".to_string();
    for _ in 1..=4 {
        let cloned_data = data.clone();
        let cloned_more_data = more_data.clone();
        thread::spawn(move || foo(cloned_data, cloned_more_data));
    }
}

fn foo(_data: Vec<u64>, _more_data: String) {}

It does what I want, but it's very noisy and boilerplaty, especially with lots of arguments. Is there a better way?

2

2 Answers

3
votes

You could use iter::repeat() to clone the data for you:

use std::thread;

fn main() {
    let data = vec![42; 10];
    let more_data = "Important data".to_string();
    let threads: Vec<_> = std::iter::repeat((data, more_data))
        .take(5)
        .map(|(data, more_data)| thread::spawn(|| foo(data, more_data)))
        .collect();
    let result: Vec<_> = threads.into_iter().map(|x| x.join()).collect();
    result.iter().for_each(|x| {
        println!("{:?}", x);
    });
}

fn foo(_data: Vec<u64>, _more_data: String) -> u64 {
    println!("Hello");
    42
}
2
votes

Is there a better way?

Nope, not using the standard library. There's a proto-RFC about supporting some syntax for this, but that's a far way from happening. It does have multiple solutions that you can use today, such as the enclose macro

macro_rules! enclose {
    ( ($( $x:ident ),*) $y:expr ) => {
        {
            $(let $x = $x.clone();)*
            $y
        }
    };
}

This allows you to write your code like this:

enclose!((data, more_data) {
    thread::spawn(move || foo(data, more_data));
})