Concurrent Rust
Write fearless concurrent Rust — threads, channels, Send/Sync, Arc/Mutex, scoped threads, and data-parallel iterators with Rayon.
View badge details
About This Course
Rust's ownership model becomes a superpower when you add concurrency. This course teaches you to write fearless concurrent code threads, channels, shared state, scoped threads, and data-parallel iterators with Rayon without the data races and deadlocks that plague other languages. You will learn why Send and Sync are the compiler's thread-safety contract, when a Mutex beats a channel, when a channel beats a Mutex, and when Rayon's work-stealing scheduler outruns both. By the end of this course you will be able to pick the right concurrency primitive for a given workload (thread pool, channel pipeline, Arc<Mutex<T>>, scoped threads, or par_iter), diagnose and fix any compiler-caught thread-safety error, build a producer/consumer pipeline with mpsc channels, share state across threads safely with Arc<Mutex<T>> and Arc<RwLock<T>>, and parallelize CPU-bound work with Rayon. Prerequisites: Courses 1-4 of the Programming with Rust skill path or equivalent fluency with ownership, borrows, traits, and iterators. Follows the Rust 2024 edition on stable Rust 1.99.
Course Curriculum
15 Lessons
Threads in Rust — spawn, join, move closures
In this lesson you will learn how Rust models operating-system threads with the standard library, what makes a closure safe to send to a thread, and how Rust's ownership model turns thread lifetime management into a type-checker job. By the end of this lesson you will be able to spawn an OS thread, pass owned data into it with a move closure, wait for it to finish with a join handle, and explain when threads are the right tool versus asynchronous tasks or data-parallel iterators.
Spawn, join, and share data by move - Lab Exercises
In this lab you will practice spawning threads, moving owned data into them, and joining to collect their results. By the end of this lab you will be able to split a workload across multiple threads with explicit move closures, aggregate per-thread results back on the main thread, and recognize the compiler errors that save you from accidentally sharing non-thread-safe data.
Channels and message passing
In this lesson you will learn how Rust threads coordinate without shared memory using multi-producer single-consumer channels. By the end of this lesson you will be able to describe the difference between unbounded and bounded channels, explain how sender cloning and receiver drop create natural shutdown signals, and pick between a channel and a shared mutex for a given coordination problem.
Multi-producer single-consumer pipeline - Lab Exercises
In this lab you will build a three-stage pipeline — producers, parsers, and a collector — connected by multi-producer single-consumer channels. By the end of this lab you will be able to wire multiple producer threads into a shared channel, parse messages in worker threads, drain a receiver with iteration, and shut down the pipeline cleanly by dropping the senders.
Send and Sync — the thread-safety contract
In this lesson you will learn the two marker traits that make Rust's thread-safety story work. By the end of this lesson you will be able to explain what the Send and Sync traits mean operationally, name the common non-thread-safe types and why they are not safe, trace how marker traits propagate through generic and composite types, and read any Send or Sync compiler error with confidence.
Fix the Send/Sync errors - Lab Exercises
In this lab you will work through a set of small programs that each fail to compile because of a Send or Sync violation. By the end of this lab you will be able to recognize the common patterns that produce thread-safety errors, pick the right fix for each pattern, and read any Send-or-Sync compiler diagnostic without reaching for a search engine.
Shared mutable state — Mutex, RwLock, Arc
In this lesson you will learn how Rust threads share mutable state safely using the standard-library synchronization primitives. By the end of this lesson you will be able to pick between a mutex and a reader-writer lock for a given workload, explain the guard pattern and how it prevents forgotten-release bugs, recognize and avoid deadlocks and lock poisoning, and know when to reach for atomic reference counting versus plain cloning.
Shared registry with Arc, Mutex, RwLock - Lab Exercises
In this lab you will build a shared station registry that multiple threads read from and write to concurrently. By the end of this lab you will be able to wrap a shared data structure in an atomic reference counter, protect it with the right synchronization primitive for the workload, measure the throughput difference between mutex and reader-writer lock on a read-heavy workload, and recognize the common deadlock patterns.
Scoped threads — borrowing stack data
In this lesson you will learn how scoped threads let you borrow stack data from worker threads without reaching for an atomic reference counter. By the end of this lesson you will be able to explain the lifetime guarantee that makes scoped threads sound, pick between scoped threads and spawned threads for a given problem, and write parallel aggregation code that borrows shared read-only data directly.
Scoped parallel aggregation - Lab Exercises
In this lab you will aggregate a batch of records across multiple threads using scoped threads to borrow shared data from the stack. By the end of this lab you will be able to split a workload with explicit chunks, borrow immutable shared data across threads without atomic reference counting, and compare the ergonomics of scoped threads against the spawn-and-join pattern from earlier labs.
Rayon — par_iter, par_bridge, work-stealing
In this lesson you will learn Rayon, the Rust ecosystem's data-parallel iterator library. By the end of this lesson you will be able to parallelize a plain iterator chain with a one-line change, bridge non-indexed iterators onto parallel execution, explain how the work-stealing scheduler balances load across cores, and predict when parallel iteration will win against explicit threads and when it will not.
Parallel window aggregator with Rayon - Lab Exercises
In this lab you will compute per-key statistics over a large record set in parallel using Rayon. By the end of this lab you will be able to parallelize an iterator chain with a one-token change, bridge a non-indexed iterator onto parallel execution, compare serial and parallel timing on the same workload, and pick between parallel iteration and explicit threads based on measured results.
Concurrency patterns — pools, pipelines, atomics
In this lesson you will learn the small set of concurrency patterns that compose into any larger concurrent Rust system. By the end of this lesson you will be able to explain the thread-pool pattern and when it beats spawning threads per task, pick between a mutex-protected counter and an atomic counter, read the two atomic memory orderings you need in practice, and recognize the deadlock-avoiding lock-ordering discipline.
Build your own thread pool - Lab Exercises
In this lab you will build a thread pool from first principles — a reusable set of worker threads behind a shared job queue. By the end of this lab you will be able to design a thread pool with a configurable worker count, submit jobs over a bounded channel, implement graceful shutdown by closing the channel and joining each worker, and reason about the backpressure behavior under load.
Capstone — real-time telemetry pipeline - Lab Exercises
In this capstone you will combine every concurrency primitive from the course into one real-time telemetry pipeline. By the end of this lab you will be able to wire ingest threads behind a bounded channel for backpressure, share a station registry across workers with an atomic reference counter and a reader-writer lock, aggregate per-window statistics with parallel iteration, maintain atomic counters for observability, and shut down the whole pipeline gracefully.