Skip to content

Channels

Rust’s standard library provides multi-producer, single-consumer (mpsc) channels for sending values between threads. A channel has two ends:

  • tx — the transmitter (sender). You can clone it to create multiple producers.
  • rx — the receiver. There is exactly one receiver per channel.
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
tx.send(42).unwrap();
let value = rx.recv().unwrap(); // blocks until a value arrives
println!("Received: {}", value);
}

mpsc::channel() returns the (Sender<T>, Receiver<T>) pair. The type T is inferred from the first send call.

The transmitter is Send, so you can move it into a thread closure. Once tx.send(value) is called, the value is moved into the channel — the sending thread no longer owns it.

use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let s = String::from("hello from thread");
tx.send(s).unwrap();
// println!("{}", s); // would not compile — s was moved into the channel
});
let received = rx.recv().unwrap();
println!("Main got: {}", received);
}

Clone tx to create multiple senders. Each clone has its own handle to the same channel. The receiver sees messages in the order they arrive (non-deterministic across threads), so collect and sort before printing.

use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
let mut handles = Vec::new();
for i in 0..3 {
let tx = tx.clone(); // each thread gets its own sender clone
handles.push(thread::spawn(move || {
tx.send(format!("message {}", i)).unwrap();
}));
}
drop(tx); // drop the original tx so the channel closes when all clones are gone
for h in handles {
h.join().unwrap();
}
let mut msgs: Vec<String> = rx.iter().collect(); // collect until channel closes
msgs.sort();
for m in &msgs {
println!("{}", m);
}
}

rx.iter() yields values until all Sender handles are dropped. Dropping the original tx (after cloning) is essential so the iterator knows when to stop.

A single producer can send work items to a pool of threads. Each worker receives items through its own channel.

use std::thread;
use std::sync::mpsc;
fn main() {
let (main_tx, main_rx) = mpsc::channel::<String>();
let mut handles = Vec::new();
for worker_id in 0..3 {
let (work_tx, work_rx) = mpsc::channel::<i32>();
let main_tx = main_tx.clone();
handles.push(thread::spawn(move || {
let item = work_rx.recv().unwrap();
main_tx.send(format!("worker {} processed {}", worker_id, item * 2)).unwrap();
}));
work_tx.send(worker_id * 10).unwrap();
}
drop(main_tx);
for h in handles {
h.join().unwrap();
}
let mut results: Vec<String> = main_rx.iter().collect();
results.sort();
for r in &results {
println!("{}", r);
}
}
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
let mut handles = Vec::new();
for i in 0..5 {
let tx = tx.clone();
let handle = thread::spawn(move || {
let msg = format!("result from thread {}", i);
tx.send(msg).unwrap();
});
handles.push(handle);
}
drop(tx);
for h in handles {
h.join().unwrap();
}
let mut msgs: Vec<String> = rx.iter().collect();
msgs.sort();
for m in &msgs {
println!("{}", m);
}
}
What does mpsc stand for in std::sync::mpsc?
What happens to a value after tx.send(value) is called?
Why must the original tx be dropped (drop(tx)) before collecting from rx.iter()?
How do you create multiple producers for a single receiver?