std.sync.mpsc
std::sync::mpsc (Multi-Producer, Single-Consumer) is the standard library module providing thread-safe message-passing channels. Its internal implementation (powered by the crossbeam-channel algorithm in modern rustc) uses lock-free array-backed linked blocks for high throughput and low allocation overhead.
Core Types in std::sync::mpsc
| Type | Role | Thread-Safety Traits (Send / Sync / Clone) |
|---|---|---|
Sender<T> |
Sending half of an unbounded channel() |
Send + Sync + Clone (when T: Send) |
SyncSender<T> |
Sending half of a bounded sync_channel(cap) |
Send + Sync + Clone (when T: Send) |
Receiver<T> |
Single receiving half for both channel kinds | Send (when T: Send), !Sync, !Clone |
SendError<T> |
Error returned when .send(val) fails because Receiver was dropped |
Unwraps original message val: T via .0 |
TrySendError<T> |
Error returned by SyncSender::try_send |
Variants: Full(T) or Disconnected(T) |
RecvError |
Error returned by .recv() when all Senders have dropped and queue is empty |
Unit struct |
TryRecvError |
Error returned by .try_recv() |
Variants: Empty or Disconnected |
RecvTimeoutError |
Error returned by .recv_timeout() |
Variants: Timeout or Disconnected |
Iter<'_, T> / TryIter<'_, T> / IntoIter<T> |
Blocking and non-blocking iterators over received messages | Created via .iter(), .try_iter(), .into_iter() |
Why Sender<T> and SyncSender<T> Are Send + Sync While Receiver<T> Is !Sync
In modern Rust (stabilized in Rust 1.72), both Sender<T> and SyncSender<T> implement Send and Sync (whenever T: Send), so threads can either clone a Sender<T> or share &Sender<T> directly across scoped threads.
In contrast, Receiver<T> is Send (can be moved to a dedicated consumer thread) but !Sync and !Clone, because the internal consumer cursor is designed for a single reader without multi-consumer lock overhead.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel::<u32>();
// Because `Sender<T>: Sync` (when `T: Send`), scoped threads can even borrow `&tx`
// directly without cloning `tx`!
thread::scope(|s| {
for i in 1..=3 {
let tx_ref = &tx;
s.spawn(move || {
tx_ref.send(i * 100).unwrap();
});
}
});
// Drop `tx` so the receiver iterator knows no more messages will arrive:
drop(tx);
let mut received: Vec<u32> = rx.into_iter().collect();
received.sort();
println!("received via mpsc: {:?}", received);
}
received via mpsc: [100, 200, 300]
Recovering Unsent Data from SendError<T>
If the Receiver<T> has already been dropped when a producer calls tx.send(value), the channel does not drop or lose value—it returns Err(SendError(value)) so the producer can recover ownership of value (err.0) and log, retry, or persist it locally:
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel::<String>();
drop(rx); // Receiver disconnects immediately
let important_msg = String::from("unsent audit record");
if let Err(send_err) = tx.send(important_msg) {
let recovered: String = send_err.0;
println!("recovered unsent message from SendError: {:?}", recovered);
}
}
recovered unsent message from SendError: "unsent audit record"