Module 9 • Lesson 46

Message Passing with Channels

📚 9 min💻 Free🦀 nixus.pro

Channels: Message Passing

Channels implement the "do not communicate by sharing memory; share memory by communicating" philosophy. Safe by design.

use std::sync::mpsc; // Multiple Producer, Single Consumer
use std::thread;

fn main() {
    // Create channel: tx = sender, rx = receiver
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let msgs = vec!["hello", "world", "from", "thread"];
        for msg in msgs {
            tx.send(msg).unwrap();
        }
        // tx dropped here - channel closes
    });

    // Receive in main thread
    for received in rx { // rx acts as an iterator!
        println!("Received: {}", received);
    }

    // Multiple producers
    let (tx2, rx2) = mpsc::channel::();
    let tx3 = tx2.clone(); // Clone sender for second producer

    thread::spawn(move || { tx2.send("from thread 1".to_string()).unwrap(); });
    thread::spawn(move || { tx3.send("from thread 2".to_string()).unwrap(); });

    drop(tx2); // We need to drop all senders for the loop to end
    // Actually: tx2 and tx3 are moved - both dropped when threads end
    for msg in rx2 { println!("{}", msg); }

    // Sync channel: bounded buffer (back-pressure)
    let (stx, srx) = mpsc::sync_channel::(10); // Buffer 10 items
    thread::spawn(move || {
        for i in 0..100 { stx.send(i).unwrap(); } // Blocks when buffer full
    });
    for _ in 0..10 { println!("{}", srx.recv().unwrap()); }
}

🎯 Practice

  1. Implement a work queue: main sends tasks via channel, N worker threads receive and process them
  2. Implement a pipeline: thread1 produces numbers, thread2 doubles them, thread3 prints results

🎉 Key Takeaways