Concurrency
OS Threads
Section titled “OS Threads”Rust’s std::thread module provides a 1:1 mapping to OS threads. Each thread gets its own stack (default 8 MB on Linux, configurable) and is scheduled by the operating system.
Spawning Threads
Section titled “Spawning Threads”use std::thread;use std::time::Duration;
let handle = thread::spawn(|| { for i in 1..=5 { println!("spawned thread: {}", i); thread::sleep(Duration::from_millis(1)); }});
for i in 1..=3 { println!("main thread: {}", i); thread::sleep(Duration::from_millis(1));}
handle.join().unwrap();Moving Data into Threads
Section titled “Moving Data into Threads”The closure passed to thread::spawn must own all captured values or borrow them for 'static. The move keyword transfers ownership into the thread’s closure:
use std::thread;
let s = String::from("hello");let handle = thread::spawn(move || { println!("{}", s); // s is moved into this closure});
handle.join().unwrap();// s is no longer valid here, it was movedWithout moveThe closure would attempt to borrow sBut the borrow checker cannot guarantee That the spawned thread will not outlive s (the thread might run after s is dropped).
Thread Return Values
Section titled “Thread Return Values”JoinHandle<T> allows the spawning thread to receive the return value:
use std::thread;
let handle = thread::spawn(|| { let mut sum = 0; for i in 1..=100 { sum += i; } sum});
let result = handle.join().unwrap();assert_eq!(result, 5050);join() blocks the calling thread until the spawned thread completes. If the spawned thread panics, join() returns Err containing the panic payload.
Scoped Threads
Section titled “Scoped Threads”std::thread::scope (stable since Rust 1.63) allows spawning threads that can borrow data from the Parent scope without move or 'static:
use std::sync::Mutex;use std::thread;
let data = vec![1, 2, 3, 4, 5];
let mut results = Mutex::new(Vec::new());
thread::scope(|s| { for chunk in data.chunks(2) { let chunk = chunk.to_vec(); s.spawn(|| { let sum: i32 = chunk.iter().sum(); results.lock().unwrap().push(sum); }); }}); // all spawned threads are joined here
assert_eq!(results, vec![3, 7, 5]);The key guarantee: all threads spawned within scope are joined before scope returns. This means Borrowed data is guaranteed to be valid for the lifetime of the scoped threads, eliminating the need for 'static bounds.
Intuition
Section titled “Intuition”Rust’s concurrency safety comes from its ownership system extended to threads. The Send marker trait indicates a type can be transferred between threads, while Sync indicates it can be shared. Channels provide message-passing concurrency, and Arc<Mutex
Cross-References
Section titled “Cross-References”- [[rust/02-ownership-borrowing/ownership]] - Ownership across thread boundaries
- [[rust/02-ownership-borrowing/interior-mutability]] - Mutex and RwLock for shared state
- [[rust/05-traits-generics/traits-and-generics]] - Send and Sync trait bounds
- [[rust/06-concurrency/channels-and-message-passing]] - Channel-based communication patterns