5

I have trouble compiling this program:

use std::env;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    let args: Vec<_> = env::args().skip(1).collect();

    let (tx, rx) = mpsc::channel();

    for arg in &args {
        let t = tx.clone();

        thread::spawn(move || {
            thread::sleep(Duration::from_millis(50));
            let _new_arg = arg.to_string() + "foo";
            t.send(arg);
        });
    }

    for _ in &args {
        println!("{}", rx.recv().unwrap());
    }
}

I read all arguments from the command line and emulate doing some work on each argument in the thread. Then I print out the results of this work, which I do using a channel.

error[E0597]: `args` does not live long enough
  --> src/main.rs:11:17
   |
11 |     for arg in &args {
   |                 ^^^^ does not live long enough
...
24 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

If I understood well.. the lifetime of args must be static (i.e. the entire time of program execution), while it only lives within the scope of main function (?). I don't understand the reason behind this, and how I could fix it.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ernest
  • 8,701
  • 5
  • 40
  • 51

1 Answers1

10

The problem lies in spawning a background thread. When you call thread::spawn you effectively have to pass ownership of any resource used in it to the thread, as it might run indefinitely, which means that its lifetime must be 'static.

There are two options to resolve that: the simplest one would be to pass ownership. Your code here

let new_arg = arg.to_string() + "foo";
t.send(arg);

looks like you actually wanted to send new_arg, in which case you could just create the owned result of arg.to_string() before spawning the thread, thus eliminating the need to pass the reference arg.

Another slightly more involved idea, that might be useful at some point though, are scoped threads as implemented in crossbeam for example. These are bound to an explicit scope, where you spawn them and are joined together at the end. This looks somewhat like this:

crossbeam::scope(|scope| {
    scope.spawn(|| {
        println!("Hello from a scoped thread!");
    });
});

Have a look at the docs for further details.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Emilia Bopp
  • 866
  • 10
  • 20
  • 2
    Since the vector is a collection of `String`s and not used after spawning the threads, I'd just move `arg` as-is into the thread - [example](http://is.gd/CMhQ7t). In fact, I might even eschew the collection into the `Vec` at all - [example](http://is.gd/vQcgWI). – Shepmaster Dec 23 '15 at 17:46
  • Thank you @Shepmaster and aepsil0n, it really clicked for me this time. [crossbeam::Scope.spawn()](http://aturon.github.io/crossbeam-doc/crossbeam/struct.Scope.html#method.spawn) also nicely explains this very problem. – Ernest Dec 23 '15 at 22:27