Thuta Learning
Rust
AdvancedProgrammingbeginner

Concurrency — Threads and Channels

What you'll walk away with

  • Explain the core ideas behind Concurrency — Threads and Channels
  • Run the sample Rust code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

Running an OS thread with `std::thread::spawn` requires passing it a closure — if that closure captures a variable from the main thread, the `move` keyword must be added explicitly, moving ownership entirely to the spawned thread; the main thread can no longer use that variable afterward, applying Lesson 6's ownership rule right across a thread boundary. The `Send` trait (safe to transfer ownership from one thread to another) and `Sync` trait (safe for multiple threads to access a reference concurrently) are automatically implemented for most types, but types like `Rc<T>` deliberately don't implement them, so trying to use `Rc<T>` across threads triggers an immediate compile error — this is Rust's signature guarantee that turns a data race, normally an intermittent and notoriously hard-to-debug runtime bug, into a compile-time error instead. `mpsc` (multiple producer, single consumer) channels are a pattern for threads to communicate by passing messages rather than sharing mutable state that needs locks — `tx.send(value)` moves ownership into the channel, and `rx.recv()` retrieves it on the other end — similar to Go's CSP philosophy of "communicate by sharing memory" done the opposite way, except Rust enforces the ownership move at compile time, so the sender can never use that data again once it's been sent through the channel.

Connect it to a real scenario

Previewing the concurrent batch processor (Lesson 23's project), you can split a batch of lesson files across multiple threads with `std::thread::spawn` for word counting — moving the file list into a `move` closure means the main thread can no longer reference that list afterward. To send each thread's word count result back to the main thread, create `mpsc::channel()` and clone `tx` for every thread — each thread sends its result with `tx.send(count)`, and the main thread accumulates them with `rx.iter()`, aggregating results without needing any shared `Mutex` state at all. Trying to share `Rc<Vec<String>>` (from Lesson 17) across threads produces an immediate compile error, so `Arc<Vec<String>>` (atomic reference count) must be used instead.

Try the working example

rust
use std::sync::mpsc;
use std::thread;

fn main() {
    let (tx, rx) = mpsc::channel();

    for batch in [vec!["a", "bb", "ccc"], vec!["dddd", "e"]] {
        let tx = tx.clone();
        thread::spawn(move || {
            let count: usize = batch.iter().map(|w| w.len()).sum();
            tx.send(count).unwrap();
        });
    }
    drop(tx);

    let total: usize = rx.iter().sum();
    println!("total chars: {total}");
}
You should see
You print "total chars: 11", showing two threads sent results through the channel and the main thread accumulated them.

5-minute try-it

Spawn three threads with `thread::spawn` — have each send a numeric value (of your choosing) back to the main thread through an `mpsc::channel` — have the main thread sum the results with `rx.iter().sum()`.

One important caution

Trying to borrow an outer variable inside a `thread::spawn` closure without the `move` keyword — since the spawned thread might outlive the main thread, a borrowed reference could dangle, so the compiler blocks it with an immediate compile error.

Trying to share one `mpsc::Sender` (`tx`) across multiple threads without cloning it — each thread needs its own copy via `tx.clone()`, otherwise you run into ownership-move problems.

The Rust Programming Language — Fearless ConcurrencyRust

Easy traps

  • Trying to borrow an outer variable inside a `thread::spawn` closure without the `move` keyword — since the spawned thread might outlive the main thread, a borrowed reference could dangle, so the compiler blocks it with an immediate compile error.
  • Trying to share one `mpsc::Sender` (`tx`) across multiple threads without cloning it — each thread needs its own copy via `tx.clone()`, otherwise you run into ownership-move problems.
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Spawn three threads with `thread::spawn` — have each send a numeric value (of your choosing) back to the main thread through an `mpsc::channel` — have the main thread sum the results with `rx.iter().sum()`.

You'll know it worked when: You print "total chars: 11", showing two threads sent results through the channel and the main thread accumulated them.

Concurrency — Threads and Channels | Thuta Learning