1
votes

Given

fn greet(peeps: &str) {
    println!("Hello, {}", peeps);
}

I can do:

fn main() {
    let a = "World";
    thread::spawn(move || greet(a)).join().unwrap();
}

The compiler understands that the thread does not outlive the borrowed string, but this is merely a special case when the lifetime of the &str is known to be 'static. When I try to do the same with a function argument, it does not compile:

fn indirect(peeps: &str) {
    thread::spawn(move || greet(&peeps)).join().unwrap();
    // Does not compile, for fear that the thread may outlive peeps
}

However, to a human reader, it is obviously the case that the thread cannot outlive the borrowed string.

I have found two workarounds:

  1. Make a copy of the string, which can be moved into the thread:

    fn indirect(peeps: &str) {
        let peeps = peeps.to_string();
        thread::spawn(move || greet(&peeps)).join().unwrap();
    }
    
  2. or, make use of the famously deprecated thread::scoped:

    #![feature(scoped)]
    fn indirect_scoped(peeps: &str) {
        thread::scoped(move || greet(&peeps)).join();
    }
    

I don't want to specify 'static lifetime for the function parameter, I'd prefer not to make an unnecessary copy (workaround 1) and I'd prefer not to use deprecated features (workaround 2).

What should I do in this situation?

1
It's not clear whether it would suit your use case, but Arc<String> could also be a solution to this problem. It would require calling to_string() at some point given a &str, but then it could be shared across many threads without copying.BurntSushi5
"I'd prefer not to..." -- very understandable. I hope something like scoped will come back into the standard library.sellibitze

1 Answers

7
votes

The approach with scoped() is the correct way when you want to pass borrowed data to child threads. While thread::scoped() itself is deprecated due to its unsoundness, an alternative sound APIs like crossbeam or scoped_threadpool provide a way to do this on stable Rust:

extern crate crossbeam;

fn indirect(peeps: &str) {
    crossbeam::scope(|scope| {
        scope.spawn(|| greet(peeps));
    });
}