SyntaxStudy
Sign Up
Rust Message Passing with Channels
Rust Beginner 1 min read

Message Passing with Channels

Rust's standard library provides multi-producer, single-consumer (mpsc) channels for message passing between threads. The `std::sync::mpsc::channel()` function returns a `(Sender, Receiver)` pair. Multiple senders can be created by cloning the `Sender`; there is only one `Receiver`. Data sent through a channel is moved from the sender to the receiver, transferring ownership and ensuring no two threads share the data. The `Sender::send(value)` method sends a value and returns `Ok(())` or `Err` if the receiver has been dropped. The `Receiver::recv()` method blocks until a message arrives; `try_recv()` returns immediately with an error if no message is available; `recv_timeout(duration)` waits up to a specified duration. The receiver can be used in a `for msg in rx` loop, which iterates until all senders have been dropped. The channel-based concurrency model aligns with the CSP (Communicating Sequential Processes) philosophy made famous by Go: instead of sharing memory and coordinating access with locks, threads communicate by sending values through channels. Rust supports both models — channels for message passing and `Arc>` for shared state — giving you the tools to choose the right pattern for each problem.
Example
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

fn main() {
    // --- Basic channel ---
    let (tx, rx) = mpsc::channel::<String>();

    let tx_clone = tx.clone();
    thread::spawn(move || {
        tx.send("message from thread 1".to_string()).unwrap();
    });
    thread::spawn(move || {
        tx_clone.send("message from thread 2".to_string()).unwrap();
    });

    // recv blocks until a message arrives
    for _ in 0..2 {
        println!("received: {}", rx.recv().unwrap());
    }

    // --- Pipeline with worker threads ---
    let (jobs_tx, jobs_rx) = mpsc::channel::<u64>();
    let (results_tx, results_rx) = mpsc::channel::<u64>();

    // Worker: receives numbers, sends squares
    thread::spawn(move || {
        for n in jobs_rx {
            thread::sleep(Duration::from_millis(5));
            results_tx.send(n * n).unwrap();
        }
    });

    // Send jobs
    for i in 1..=5 {
        jobs_tx.send(i).unwrap();
    }
    drop(jobs_tx);  // close the channel so the worker loop ends

    // Collect results via for loop (ends when all senders are dropped)
    let squares: Vec<u64> = results_rx.iter().collect();
    println!("squares: {:?}", squares);

    // --- try_recv: non-blocking ---
    let (tx2, rx2) = mpsc::channel::<i32>();
    tx2.send(42).unwrap();
    match rx2.try_recv() {
        Ok(v) => println!("try_recv got: {v}"),
        Err(e) => println!("try_recv error: {e}"),
    }
}