Threads
| thread::spawn | |
| Join handles | |
| Move into threads | |
| Scoped threads note |
thread::spawn
Spawn starts a closure on a new operating system thread and returns at once. The main thread keeps running beside it with no waiting. Output below may vanish when main exits first, and the next section fixes that.
use std::thread;
fn main() {
thread::spawn(|| {
println!("hello from a thread");
});
}
The closure needs no parameters here, hence the empty bars. Spawned threads run truly in parallel on separate cores when available. Never rely on timing alone to see their results.
Join handles
Every spawn hands back a join handle that represents the running thread. Join blocks until that thread ends and returns whatever the closure produced. Unwrap surfaces a panic from the child as an error in the parent.
use std::thread;
fn main() {
let handle = thread::spawn(|| {
2 + 2
});
let answer = handle.join().unwrap();
println!("{}", answer);
}
The handle owns the thread outcome, so dropping it without joining detaches the work. Answer holds the plain integer once join succeeds. Joining every handle keeps shutdown order predictable.
Move into threads
Threads outlive the scope that starts them, so borrowed captures are forbidden. The move keyword transfers ownership of used values into the closure. Name travels into the thread here, and the outer binding stays gone afterwards.
use std::thread;
fn main() {
let name = String::from("ada");
let handle = thread::spawn(move || {
println!("hello, {}", name);
});
handle.join().unwrap();
}
Move applies to every capture at once, not just one variable. Values that implement Copy duplicate instead of moving. Static bounds plus move together guarantee the thread never dangles.
Scoped threads note
Scoped threads lift the static restriction for threads that provably end in time. The scope waits for every child before returning, so borrowing the surroundings is safe. Nums below is only borrowed, and the scope proves it outlives the read.
use std::thread;
fn main() {
let nums = vec![1, 2, 3];
thread::scope(|s| {
s.spawn(|| {
println!("{:?}", nums);
});
});
}
Scope joins all children automatically at its closing brace, even on panic paths. Borrowing avoids clones and Arcs for short parallel bursts. Prefer scoped threads whenever the work fits inside one function body.
Next: Mutex and Arc
Article author: Arthur Isaev