#285 Jul 27, 2026

285. try_iter — The Drain Loop You Wrote This Morning, as One Line

That match-on-try_recv drain loop from bite 284? The standard library already wrote it for you: try_iter yields every pending message and stops the moment the channel is empty.

Bite 284 drained a channel by hand: loop, try_recv, match, break on Empty. That shape is so common it’s an iterator:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
use std::sync::mpsc;

let (tx, rx) = mpsc::channel();

for i in 0..3 {
    tx.send(i).unwrap();
}

// drain everything pending — never blocks
let drained: Vec<_> = rx.try_iter().collect();
assert_eq!(drained, [0, 1, 2]);

// queue empty now — iterator just ends
assert_eq!(rx.try_iter().next(), None);

Unlike rx.iter(), which blocks on every next() until a message arrives, try_iter never waits. Empty channel means the iterator simply ends — perfect for the frame loop, which collapses from six lines to two:

1
2
3
4
5
6
loop {
    for msg in rx.try_iter() {
        apply(msg);
    }
    render_frame();
}

The trade-off: try_iter flattens both Empty and Disconnected into plain None. The exit signal bite 284 relied on is invisible here:

1
2
3
4
5
6
7
8
9
use std::sync::mpsc::TryRecvError;

drop(tx); // last sender gone

// still just None — the disconnect is swallowed
assert_eq!(rx.try_iter().next(), None);

// ask try_recv when you need the real reason
assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected));

So a loop that must notice dead senders can drain with try_iter, then make one try_recv call to check for Disconnected — or just keep the explicit match from bite 284.

Rule of thumb: try_iter when “no messages” and “no more senders” mean the same thing to you; try_recv when disconnection is your signal to stop.

← Previous 284. try_recv — Poll the Channel Without Stalling the Loop