Expand description
Threads that can borrow variables from the stack.
Create a scope when spawned threads need to access variables on the stack:
use crossbeam_utils::thread;
let people = vec![
"Alice".to_string(),
"Bob".to_string(),
"Carol".to_string(),
];
thread::scope(|s| {
for person in &people {
s.spawn(move |_| {
println!("Hello, {}!", person);
});
}
}).unwrap();
Why scoped threads?
Suppose we wanted to re-write the previous example using plain threads:
use std::thread;
let people = vec![
"Alice".to_string(),
"Bob".to_string(),
"Carol".to_string(),
];
let mut threads = Vec::new();
for person in &people {
threads.push(thread::spawn(move || {
println!("Hello, {}!", person);
}));
}
for thread in threads {
thread.join().unwrap();
}
This doesn’t work because the borrow checker complains about people
not living long enough:
error[E0597]: `people` does not live long enough
--> src/main.rs:12:20
|
12 | for person in &people {
| ^^^^^^ borrowed value does not live long enough
...
21 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
The problem here is that spawned threads are not allowed to borrow variables on stack because
the compiler cannot prove they will be joined before people
is destroyed.
Scoped threads are a mechanism to guarantee to the compiler that spawned threads will be joined before the scope ends.
How scoped threads work
If a variable is borrowed by a thread, the thread must complete before the variable is
destroyed. Threads spawned using std::thread::spawn
can only borrow variables with the
'static
lifetime because the borrow checker cannot be sure when the thread will complete.
A scope creates a clear boundary between variables outside the scope and threads inside the scope. Whenever a scope spawns a thread, it promises to join the thread before the scope ends. This way we guarantee to the borrow checker that scoped threads only live within the scope and can safely access variables outside it.
Nesting scoped threads
Sometimes scoped threads need to spawn more threads within the same scope. This is a little
tricky because argument s
lives inside the invocation of thread::scope()
and as such
cannot be borrowed by scoped threads:
use crossbeam_utils::thread;
thread::scope(|s| {
s.spawn(|_| {
// Not going to compile because we're trying to borrow `s`,
// which lives *inside* the scope! :(
s.spawn(|_| println!("nested thread"));
});
});
Fortunately, there is a solution. Every scoped thread is passed a reference to its scope as an argument, which can be used for spawning nested threads:
use crossbeam_utils::thread;
thread::scope(|s| {
// Note the `|s|` here.
s.spawn(|s| {
// Yay, this works because we're using a fresh argument `s`! :)
s.spawn(|_| println!("nested thread"));
});
}).unwrap();
Structs
- A scope for spawning threads.
- A handle that can be used to join its scoped thread.
- Configures the properties of a new thread.
Functions
- Creates a new scope for spawning threads.