Thread lifetime issue

Hi, I am trying to do a very simple threads program. I am stuck on:

   Compiling thread_try v0.1.0 (D:\mor2\rust\thread_try)
error[E0597]: `d` does not live long enough
  --> src\main.rs:35:13
   |
35 |     analyze(&d);
   |     --------^^-
   |     |       |
   |     |       borrowed value does not live long enough
   |     argument requires that `d` is borrowed for `'static`
36 | }
   | - `d` dropped here while still borrowed

error[E0382]: use of moved value: `results`
  --> src\main.rs:53:36
   |
40 |     let mut results: Vec<Res> = Vec::new();
   |         ----------- move occurs because `results` has type `Vec<Res>`, which does not implement the `Copy` trait
...
53 |         let handle = thread::spawn(move || {
   |                                    ^^^^^^^ value moved into closure here, in previous iteration of loop
54 |             my_process(d, start, last, &mut results);
   |                                             ------- use occurs due to use in closure

Here is the code:

use std::thread;

#[derive(Debug, Clone, Copy)]
struct Dfrow
{
    year:i32,
    index:i32
}

struct Res
{
    year:i32,
    index:i32
}

fn main() 
{
    run_me();
}


fn run_me()
{
    let mut d = Vec::new();
    
	for i in 0..1000
	{
        let d1:Dfrow = Dfrow {
            year: 2020,
            index: i
        };
		d.push(d1);
    }
    
    analyze(&d);
}

fn analyze(d: &'static Vec<Dfrow>)
{
    let mut results: Vec<Res> = Vec::new();
    let num_threads = 16;

    let len:f64 = d.len() as f64;

    let mut handles = Vec::new();

    for th in 0..num_threads
    {
        let start = (th as f64 * len / num_threads as f64) as usize;
        let last = ((th as f64 + 1.0) * len / num_threads as f64) as usize;
        println!("Task {} start {} last {}", th, start, last);

        let handle = thread::spawn(move || {
            my_process(d, start, last, &mut results);
        });
        handles.push(handle);

    }
}

fn my_process(d: &Vec<Dfrow>, start: usize, last:usize, result: &mut Vec<Res>)
{
    for i in start..last
    {
		result[i].year = d[i].year;
		result[i].index = d[i].index;
	}
}

Please, format the error message like your code block.

  1. First error: "d does not live long enough" - is mostly self-descriptive: analyze requires its argument to be a 'static reference, but &d is not 'static, since it references the local variable. I'm not sure how this can be fixed - the simplest way is to just change analyze argument to owned type, i.e. Vec<Dfrow>.

  2. Second error: "use of moved value: results" - essentially boils down to the fact that you're trying to use the same value from multiple threads mutably, without explicit sharing and locking. To do this, you have to wrap results into Arc<Mutex<_>>, and clone the Arc each time before thread::spawn.

I did as you suggested: change the owned type to no referenced and I got this:

error[E0382]: use of moved value: `d`
  --> src\analyze.rs:28:36
   |
15 | pub fn analyze1(d: Vec<Dfrow>)
   |                 - move occurs because `d` has type `Vec<Dfrow>`, which does not implement the `Copy` trait
...
28 |         let handle = thread::spawn(move || {
   |                                    ^^^^^^^ value moved into closure here, in previous iteration of loop
29 |             my_process(&d, start, last);  //, &mut my_results);
   |                         - use occurs due to use in closure

I remove the result for now to try to solve one problem at a time

This error is due to the fact that the closure captures the variables as-is. That is, the first thread::spawn captured the whole d and passed a reference to the captured value into my_progress, so the next thread::spawn has no d to capture. Try this:

let ref_d = &d; // creating the reference in the main thread
let handle = thread::spawn(move || {
    my_process(ref_d, start, last);
});

If you're interested why this worked before - that's because shared references are Copy, so each thread received its own copy of &Vec.

2 Likes

Instead of:

let ref_d = &d; // creating the reference in the main thread
let handle = thread::spawn(move || {
    my_process(ref_d, start, last);
});

I prefer:

let handle = thread::spawn({
    let ref_d = &d;
    move || {
        my_process(ref_d, start, last);
    }
});

or in the case of cloned Arc:

let handle = thread::spawn({
    let d = Arc::clone(&d);
    move || {
        my_process(d, start, last);
    }
});

Keeping variables, that are purely created for capturing, as close and (visually) local as possible to the corresponding closure makes the program more readable for me.

2 Likes

Both of the solutions with: let ref_d = &d gave me:

error[E0597]: `d` does not live long enough
  --> src\analyze.rs:30:22
   |
29 |           let handle = thread::spawn({
   |  ______________________-
30 | |             let rd = &d;
   | |                      ^^ borrowed value does not live long enough    
31 | |             move || {
32 | |                 my_process(&rd, start, last);  //, &mut my_results);
33 | |             }
34 | |         });
   | |__________- argument requires that `d` is borrowed for `'static`
...
59 |   }
   |   - `d` dropped here while still borrowed

The Arc solution did work.

Yes, that's not surprising. You cannot move a reference into a separate thread because the reference is not guaranteed to stay alive until the thread has exited.

ok, Thanks for the help.