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
- Implement a work queue: main sends tasks via channel, N worker threads receive and process them
- Implement a pipeline: thread1 produces numbers, thread2 doubles them, thread3 prints results
🎉 Key Takeaways
- mpsc channels: multiple producers (clone tx), single consumer
- rx.recv() blocks until message; rx as iterator ends when channel closes
- sync_channel has bounded buffer - sender blocks when full (back-pressure)
- Channel types enforce ownership: sent data is moved into the channel