rust.unbounded_channel
Stability
Medium
Detects usage of unbounded channels that can cause memory exhaustion.
Why It Matters
Section titled “Why It Matters”Unbounded channels grow without limit:
- Memory exhaustion — If sender outpaces receiver, queue grows forever
- OOM crash — Eventually the process is killed
- Hidden backpressure — No signal to slow down producers
- Latency spikes — Queue grows, items wait longer
Bounded channels provide natural backpressure.
Example
Section titled “Example”// ❌ Beforelet (tx, rx) = tokio::sync::mpsc::unbounded_channel();
// Sender can overwhelm receiverfor item in items { tx.send(item).unwrap(); // Never blocks}// ✅ Afterlet (tx, rx) = tokio::sync::mpsc::channel(1000); // Bounded
for item in items { tx.send(item).await.unwrap(); // Blocks when full}What Unfault Detects
Section titled “What Unfault Detects”unbounded_channel()from tokio or stdcrossbeam::channel::unbounded()flume::unbounded()
Auto-Fix
Section titled “Auto-Fix”Unfault converts to bounded channel with configurable capacity.
When Unbounded Is Acceptable
Section titled “When Unbounded Is Acceptable”// Known finite producerslet (tx, rx) = unbounded_channel();for i in 0..10 { // Fixed, small count tx.send(i).unwrap();}
// Command channels where blocking is worse// (but document the risk)let (cmd_tx, cmd_rx) = unbounded_channel(); // Commands are rareBounded Channel Sizing
Section titled “Bounded Channel Sizing”// High throughput: larger bufferlet (tx, rx) = channel(10_000);
// Low latency: smaller buffer (backpressure sooner)let (tx, rx) = channel(100);
// Memory constrained: calculate based on item sizelet capacity = MAX_MEMORY / std::mem::size_of::<Item>();let (tx, rx) = channel(capacity);