2
votes

Why doesn't Example 1 compile given that Example 2 compiles just fine? The only difference between the examples is that in Example 1 value is a function local variable, and in Example 2 value is an argument to the function.

Example 1

#![feature(scoped)]

use std::thread;
use std::sync::atomic::{AtomicUsize, Ordering};

pub fn foo<F>() {
    let mut join_guards = Vec::new();
    let value = AtomicUsize::new(0);

    for _ in 0..10 {
        join_guards.push(thread::scoped(|| {
            value.fetch_add(1, Ordering::SeqCst);
        }));
    }
}

Example 2

#![feature(scoped)]

use std::thread;
use std::sync::atomic::{AtomicUsize, Ordering};

pub fn foo<F>(value: AtomicUsize) {
    let mut join_guards = Vec::new();

    for _ in 0..10 {
        join_guards.push(thread::scoped(|| {
            value.fetch_add(1, Ordering::SeqCst);
        }));
    }
}

These are the error messages I get trying to compile Example 1:

error: `value` does not live long enough
         join_guards.push(thread::scoped(|| {
             value.fetch_add(1, Ordering::SeqCst);
         }));
note: reference must be valid for the block at 6:20...
pub fn foo<F>() {
    let mut join_guards = Vec::new();
    let value = AtomicUsize::new(0);

     for _ in 0..10 {
         join_guards.push(thread::scoped(|| {

note: ...but borrowed value is only valid for the block suffix following statement 1 at 8:40
    let value = AtomicUsize::new(0);

     for _ in 0..10 {
         join_guards.push(thread::scoped(|| {
             value.fetch_add(1, Ordering::SeqCst);
         }));
1
Note that thread::scoped was removed from the standard library because it was not memory-safe. It has been safely replaced by crates.Shepmaster

1 Answers

3
votes

join_guards is a vector of scoped threads with a certain shared lifetime. That lifetime is determined by which variables it closes over: value is the only one. Therefore, the lifetime of the scoped threads is the scope of value. Where is value legal? After it is defined and until it goes out of scope—that is, “the block suffix following statement 1 at 8:40”. join_guards must have as its lifetime a lifetime no greater than this, but if join_guards is defined before value, this is not the case.

The solution is to reverse the order of declaration:

let value = AtomicUsize::new(0);
let mut join_guards = Vec::new();

This explains why the second example works, also.