In most current operating systems, an executed program's code is run in a process, and the OS can manage multiple processes at once.
Within a program, you can also have independent parts that run simultaneously. These independent parts are called threads.
For example, a web server could have multiple threads to handle multiple incoming requests concurrently.
use std::thread;
use std::time::Duration;
fn main() {
thread::spawn(|| {
for i in 1..10 {
println!("hi number {i} from the spawned thread!");
thread::sleep(Duration::from_millis(1));
}
});
for i in 1..5 {
println!("hi number {i} from the main thread");
thread::sleep(Duration::from_millis(1));
}
}This code creates a new thread using thread::spawn, which runs in parallel with the main thread.
You may not always see all output from the spawned thread, as the main thread may finish first.
You can make the main thread wait for the spawned thread using .join():
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {i} from the spawned thread!");
thread::sleep(Duration::from_millis(1));
}
});
handle.join().unwrap(); // waits for the spawned thread to finish
for i in 1..5 {
println!("hi number {i} from the main thread");
thread::sleep(Duration::from_millis(1));
}
}Using .join() ensures that the spawned thread completes before the main thread continues.
We'll often use the move keyword with closures passed to thread::spawn because:
- Threads can outlive their spawning function.
- Variables captured in closures must be owned if they are moved into another thread.
movetransfers ownership into the thread.
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(|| {
// ❗ Error: closure may outlive the current function and `v` doesn't live long enough
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}Rust prevents this to avoid using data after it’s dropped.
use std::thread;
fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
// `v` is now owned by the thread
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}Now v is moved into the thread, and the compiler is happy.
Remember: once moved, v can no longer be used in the main thread.