Concurrency | Rust - Wyatt's Notes
import Citations from ‘@components/Citations.astro’
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 model is like a traffic system with strict rules. Each thread is a lane, and the type system is the traffic controller. Arc and Mutex are the traffic lights: Arc lets multiple threads share ownership of data, and Mutex ensures only one thread accesses it at a time. The compiler prevents data races before your program runs, which is like having a safety inspector who checks every vehicle before it enters the highway. Channels are the postal system: threads send messages instead of sharing memory, reducing contention.
<Citations sources={[ {title=“The Rust Programming Language”, author=“Klabnik and Nichols”, year=“2024”, type=“book”, url=“https://doc.rust-lang.org/book/”}, {title=“Rust for Rustaceans”, author=“Gjengset”, year=“2021”, type=“book”}, ]} />