Introduction
A Stream yields multiple values asynchronously — it's the async version of Iterator. While Iterator::next() returns Option<T> synchronously, Stream::poll_next() returns Poll<Option<T>> and can suspend between items. Note: for await syntax is still nightly-only.
Key Concepts
rustpub trait Stream { type Item; fn poll_next( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Self::Item>>; }
| Sync | Async |
|---|---|
Iterator | Stream |
next() -> Option<T> | poll_next() -> Poll<Option<T>> |
for item in iter | while let Some(item) = stream.next().await |
Real World Context
Streams model WebSocket messages, database cursors, paginated API responses, file lines, Kafka topics — any sequence of values arriving over time.
Deep Dive
Using streams with StreamExt::next():
rustuse tokio_stream::StreamExt; let mut stream = tokio_stream::iter(vec![1, 2, 3]); while let Some(item) = stream.next().await { println!("{item}"); }
Creating streams from iterators:
rustlet stream = tokio_stream::iter(1..=5);
With async_stream macro:
rustuse async_stream::stream; let s = stream! { for i in 0..3 { tokio::time::sleep(Duration::from_millis(100)).await; yield i; } };
From channels:
rustlet (tx, rx) = tokio::sync::mpsc::channel(32); let stream = tokio_stream::wrappers::ReceiverStream::new(rx);
Common Pitfalls
- Using
forloops on streams — you needwhile let Some(x) = stream.next().await(or nightlyfor await) - Forgetting to pin streams —
tokio::pin!(stream)is often needed - Not importing
StreamExt— the.next()method comes from this trait
Best Practices
- Import
StreamExtfor ergonomic stream consumption - Use
async_stream::stream!for custom stream logic - Pin streams with
tokio::pin!when needed by adapters
Summary
Streams are async iterators yielding Poll<Option<T>>. Use StreamExt::next().await to consume them. Create streams from iterators, channels, or the async_stream macro. Standard for loops don't work — use while let.
Code Examples
rust
use tokio_stream::StreamExt;
use std::time::Duration;
#[tokio::main]
async fn main() {
let stream = tokio_stream::iter(1..=5)
.throttle(Duration::from_millis(200));
tokio::pin!(stream);
while let Some(value) = stream.next().await {
println!("Got: {value}");
}
}