Threads
Spawning Threads with std::thread
Section titled “Spawning Threads with std::thread”Rust’s standard library provides OS threads through std::thread::spawn. You pass a closure, and the function returns a JoinHandle<T> — a handle you can use to wait for the thread to finish and retrieve its return value.
use std::thread;
fn main() { let handle = thread::spawn(|| { // This runs on a new OS thread 42 });
let result = handle.join().unwrap(); // block until the thread finishes println!("Thread returned: {}", result);}The closure is move || { ... } when it needs to capture variables from the enclosing scope — otherwise the compiler cannot guarantee the captured data lives long enough.
Moving Data Into a Thread
Section titled “Moving Data Into a Thread”Because a thread may outlive the scope where it was spawned, any data the thread captures must be owned by the thread. The move keyword transfers ownership of captured variables into the closure.
use std::thread;
fn main() { let message = String::from("hello from the main thread");
let handle = thread::spawn(move || { // message is moved here — the thread now owns it println!("{}", message); });
handle.join().unwrap(); // println!("{}", message); // would not compile — moved above}Sharing State with Arc<Mutex<T>>
Section titled “Sharing State with Arc<Mutex<T>>”To share mutable data between multiple threads, wrap it in Arc<Mutex<T>>:
Mutex<T>provides mutual exclusion — only one thread can lock it at a time.Arc<T>(Atomically Reference Counted) allows multiple threads to hold a reference-counted pointer to the same allocation.
You clone the Arc before moving it into each thread. The underlying Mutex is shared, not cloned.
use std::thread;use std::sync::{Arc, Mutex};
fn main() { let counter = Arc::new(Mutex::new(0_i32)); let mut handles = Vec::new();
for _ in 0..4 { let counter = Arc::clone(&counter); // cheap ref-count increment let h = thread::spawn(move || { let mut n = counter.lock().unwrap(); // acquire lock *n += 1; }); // lock released when n drops handles.push(h); }
for h in handles { h.join().unwrap(); }
println!("Final count: {}", *counter.lock().unwrap());}Collecting Results from Multiple Threads
Section titled “Collecting Results from Multiple Threads”A common pattern is to have each thread compute a value and push it into a shared Vec. After all threads complete, you sort the results so the output is deterministic regardless of scheduling order.
use std::thread;use std::sync::{Arc, Mutex};
fn main() { let results = Arc::new(Mutex::new(Vec::new())); let mut handles = Vec::new();
for i in 0..5 { let results = Arc::clone(&results); let handle = thread::spawn(move || { let square = i * i; let mut v = results.lock().unwrap(); v.push(square); }); handles.push(handle); }
for h in handles { h.join().unwrap(); }
let mut final_results = results.lock().unwrap().clone(); final_results.sort(); println!("Squares: {:?}", final_results); let sum: i32 = final_results.iter().sum(); println!("Sum: {}", sum);}Compiling…