We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Fearless Concurrency: Threads, Channels, Shared State step 1 of 24
Threads, spawn and join
Spawn n threads. Thread i computes i * i. Return the squares in index
order.
pub fn spawn_squares(n: usize) -> Vec<u64>
n = 0 gives [], n = 8 gives [0, 1, 4, 9, 16, 25, 36, 49]. Assume
n <= 256 — the grader runs on your own machine and spawning thousands of OS
threads is antisocial.
Threads in Rust are detached by default
std::thread::spawn starts an OS thread and returns a JoinHandle<T>. There
is no parent/child relationship. Nothing links the new thread’s lifetime to
the one that started it, and when main returns the process exits, killing
every thread still running. That is the single most common first-day bug: a
program that spawns work, prints nothing, and exits successfully.
The fix is join:
let handle = std::thread::spawn(|| 6 * 7);
let answer: i32 = handle.join().unwrap(); // blocks until the thread ends
Two things to notice in that one line. join blocks the calling thread
until the spawned one finishes. And it returns the closure’s value — a
thread is not a void fire-and-forget in Rust, it is a computation that hands
you a result. join gives you a std::thread::Result<T>; unwrap is fine
here because our closure cannot panic, and the next problem covers what that
Result is actually for.
The compiler will not warn you
Worth knowing before you start: dropping a JoinHandle without joining it is
not a warning. Neither rustc nor clippy -D warnings says a word about
thread::spawn(move || expensive()); // silently detached
It is legal — detaching is sometimes what you want. So this is a class of bug the toolchain does not catch for you, which is a useful early calibration on what “fearless concurrency” does and does not promise. Rust eliminates data races. It does not eliminate forgetting to wait.
Why the shape of this problem matters
Threads finish in whatever order the OS scheduler feels like. If you push
results from inside the threads into some shared collection, the order of that
collection is not yours to predict, and a test asserting [0, 1, 4, 9] would
fail at random.
The structure here avoids that entirely:
-
spawn all
nthreads, collecting the handles into aVecin index order; - then join them, in that same order, collecting the values.
Join order is your choice, not the scheduler’s, so the output is deterministic. Joining handle 3 first just means you wait a bit longer at that line — every thread still ran concurrently. Collect in join order, not in completion order is the trick this entire track is built on; you will see it again as index tagging.
Note the two-phase shape is load-bearing. If you spawn and join inside one loop you have written a sequential program with extra steps — each thread finishes before the next one starts.
Finally: println! from inside a thread will not help you here. The grader
reads the value your function returns, and stdout from a worker is not it. The
answer has to come back through join().
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.