Message Passing
Sending and Receiving Messages
Sending and receiving messages over an mpsc channel offers both blocking and non-blocking / timed methods on the transmitter and receiver halves to match different concurrency loops.
Complete Comparison of Send Methods
| Method | Receiver Type | Blocks Caller? | Return Type | When It Fails |
|---|---|---|---|---|
Sender::send(val) |
Sender<T> |
Never | Result<(), SendError<T>> |
Only if Receiver<T> has been dropped |
SyncSender::send(val) |
SyncSender<T> |
Yes (when buffer is full) | Result<(), SendError<T>> |
Only if Receiver<T> has been dropped |
SyncSender::try_send(val) |
SyncSender<T> |
Never | Result<(), TrySendError<T>> |
TrySendError::Full(val) if full, or TrySendError::Disconnected(val) if Receiver dropped |
Complete Comparison of Receive Methods (Receiver<T>)
| Method | Blocks Caller? | Return Type | Behavior When Queue Is Empty |
|---|---|---|---|
.recv() |
Yes | Result<T, RecvError> |
Parks calling thread until a message arrives or all Senders drop |
.try_recv() |
Never | Result<T, TryRecvError> |
Returns Err(TryRecvError::Empty) immediately if no message is ready (or Err(TryRecvError::Disconnected) if all Senders dropped) |
.recv_timeout(dur) |
Up to dur |
Result<T, RecvTimeoutError> |
Blocks for at most dur; returns Err(RecvTimeoutError::Timeout) if time expires |
.iter() / for msg in &rx |
Yes | Iter<'_, T> |
Yields messages until all Senders drop (keeps rx usable afterward) |
.try_iter() |
Never | TryIter<'_, T> |
Drains all currently queued messages without blocking and stops immediately when queue is empty |
Draining Currently Queued Messages Without Blocking (try_recv and try_iter)
In game loops, UI render loops, or batch-processing workers, calling rx.try_iter() drains all messages that have accumulated since the last tick without blocking the thread if the queue is empty:
use std::sync::mpsc::{self, TryRecvError};
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel::<u32>();
// Queue 3 messages before polling:
tx.send(10).unwrap();
tx.send(20).unwrap();
tx.send(30).unwrap();
// 1. Drain all currently available messages without blocking via `.try_iter()`:
let batch: Vec<u32> = rx.try_iter().collect();
println!("non-blocking drained batch: {:?}", batch);
// 2. Subsequent `.try_recv()` sees an empty queue while `tx` is still alive:
match rx.try_recv() {
Ok(val) => println!("unexpected value: {}", val),
Err(TryRecvError::Empty) => println!("try_recv: queue is currently Empty (tx still alive)"),
Err(TryRecvError::Disconnected) => println!("try_recv: channel Disconnected"),
}
// 3. Timed receive waiting at most 1 millisecond:
let timeout_res = rx.recv_timeout(Duration::from_millis(1));
println!("recv_timeout on empty queue: {:?}", timeout_res);
}
non-blocking drained batch: [10, 20, 30]
try_recv: queue is currently Empty (tx still alive)
recv_timeout on empty queue: Err(Timeout)