22.6 Channels for Message Passing

Besides shared-memory concurrency, Rust offers message passing, where threads exchange data by transferring ownership rather than sharing mutable state. This can prevent certain classes of concurrency bugs.

22.6.1 Basic Usage with std::sync::mpsc

Rust’s standard library provides an asynchronous MPSC (multiple-producer, single-consumer) channel:

use std::sync::mpsc;
use std::thread;
use std::time::Duration;

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

    thread::spawn(move || {
        for i in 0..5 {
            tx.send(i).unwrap();
            thread::sleep(Duration::from_millis(50));
        }
    });

    for received in rx {
        println!("Got: {}", received);
    }
}

When all senders are dropped, the channel closes, and the receiver’s iterator terminates.

22.6.2 Multiple Senders

Clone the transmitter to allow multiple threads to send messages:

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

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

    let tx1 = tx.clone();
    thread::spawn(move || {
        tx1.send("Hi from tx1").unwrap();
    });

    thread::spawn(move || {
        tx.send("Hi from tx").unwrap();
    });

    for msg in rx {
        println!("Received: {}", msg);
    }
}

By default, there’s one receiver. For multiple consumers or more advanced patterns, consider crates like Crossbeam or kanal.

22.6.3 Blocking and Non-Blocking Receives

  • recv() blocks until a message arrives or the channel closes.
  • try_recv() checks immediately, returning an error if there’s no data or the channel is closed.
use std::sync::mpsc::{self, TryRecvError};
use std::thread;
use std::time::Duration;

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

    thread::spawn(move || {
        for i in 0..3 {
            tx.send(i).unwrap();
            thread::sleep(Duration::from_millis(50));
        }
    });

    loop {
        match rx.try_recv() {
            Ok(value) => println!("Got: {}", value),
            Err(TryRecvError::Empty) => {
                println!("No data yet...");
            }
            Err(TryRecvError::Disconnected) => {
                println!("Channel closed");
                break;
            }
        }
        thread::sleep(Duration::from_millis(20));
    }
}

22.6.4 Bidirectional Communication

Standard channels are one-way (MPSC). For request–response patterns, you can create two channels—one for each direction—so each thread has a sender and a receiver. For multiple receivers, external crates such as Crossbeam provide MPMC (multi-producer, multi-consumer) channels.