C.W.K.
Stream
Lesson 01 of 05 · published

Threads & join

~10 min · concurrency, threads, spawn, join

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

Rust's headline promise is 'fearless concurrency' — and it's not marketing. The compiler statically prevents data races, so the entire class of 'works on my machine, corrupts data in production' threading bugs simply can't compile. This track shows how.

Spawning a thread

thread::spawn takes a closure and runs it on a new OS thread, returning a JoinHandle. The new thread runs concurrently with the one that spawned it. These are real OS threads (not green threads) — for lightweight async concurrency, that's the next track.

join: wait for completion

The spawning thread doesn't automatically wait for its children. Call .join() on the handle to block until that thread finishes and collect its result. Forget to join and main might exit while threads are still running — they'd be killed mid-work. join is how you say 'I need this thread's work before continuing.'

Threads run concurrently; join is the synchronization point. Spawn to do work in parallel, join to wait for and collect it. The handle returned by spawn is your only link to the thread — hold it if you need to wait, and .join().unwrap() to surface any panic the thread hit.

What the compiler watches

The moment a closure captures data and moves to another thread, the borrow checker's job gets harder: two threads might touch the same data at once. Rust's answer is the Send and Sync traits (last lesson of this track), which let the compiler prove, at compile time, that your sharing is race-free. Everything here rests on that guarantee.

Code

spawn a thread, join to collect its result·rust
use std::thread;

fn main() {
    let handle = thread::spawn(|| {
        for i in 1..=3 {
            println!("worker: {i}");
        }
        42 // a thread can return a value
    });

    println!("main keeps running");

    // join blocks until the thread finishes, yielding its return value
    let result = handle.join().unwrap();
    println!("thread returned {result}");
}

External links

Exercise

Spawn three threads that each compute and return the square of a different number. Collect their handles in a Vec, join each, and sum the results. What happens to the output order, and why can't you rely on it?
Hint
Threads run concurrently, so their print order is nondeterministic — the OS scheduler decides. But joining in order and collecting return values gives you deterministic results regardless of execution order.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.