Streams
Stream é o primo assíncrono de Iterator: ele produz uma sequência de itens ao longo do tempo, impulsionado por .await em vez de .next(). Use streams para APIs paginadas, WebSockets, blocos de arquivos e feeds de eventos.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
use tokio_stream::{self as stream, StreamExt};
#[tokio::main]
async fn main() {
let mut s = stream::iter(vec![10, 20, 30]);
while let Some(n) = s.next().await {
println!("{n}");
}
}Quando usar isso: Consumir HTTP paginado, cursores de banco de dados, mensagens Kafka/NATS ou eventos SSE/WebSocket.
Exemplo de Trabalho
use tokio::sync::mpsc;
use tokio_stream::{StreamExt, wrappers::ReceiverStream};
async fn produce(tx: mpsc::Sender<i32>) {
for i in 1..=5 {
tx.send(i).await.unwrap();
}
}
#[tokio::main]
async fn main() {
let (tx, rx) = mpsc::channel(8);
tokio::spawn(produce(tx));
let mut stream = ReceiverStream::new(rx);
let sum: i32 = stream.map(|n| n * 2).sum().await;
println!("{sum}");
}O que isso demonstra:
- O receptor do canal se adapta a
StreamviaReceiverStream. StreamExtfornecemap,filter,take,buffer_unordered.sum().awaitconsome o stream.
Mergulho Profundo
Como Funciona
Stream::poll_next: Semelhante aFuture::poll, mas retornaOption<Item>atéNone.- Backpressure: Consumidores lentos aplicam naturalmente pressão em canais limitados.
- Fusão com futures: Muitos aplicativos mapeiam streams para respostas (Axum SSE, gRPC streaming).
StreamExt: Métodos ergonômicos dos cratesfutures/tokio-stream.
Adaptadores Comuns
| Adaptador | Propósito |
|---|---|
map / filter | Transforma itens |
take(n) | Limita a contagem |
timeout | Prazo limite por item |
buffer_unordered(n) | Mapeamento concorrente com limite |
Notas de Rust
use futures::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
struct Counter {
n: u32,
max: u32,
}
impl Stream for Counter {
type Item = u32;
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<u32>> {
if self.n >= self.max {
Poll::Ready(None)
} else {
let v = self.n;
self.n += 1;
Poll::Ready(Some(v))
}
}
}- Implementações manuais de
Streamespelham as regras de pinning deFuture. - Prefira adaptadores em vez de implementações manuais no código da aplicação.
- A macro
try_stream!constrói streams falíveis de forma ergonômica.
Armadilhas
- Produção ilimitada - Produtor rápido sobrecarrega a memória sem limites. Correção:
mpsclimitado oustream::itercom limites de taxa. - Esquecer de impulsionar o stream - Criar um stream sem consumi-lo vaza tarefas. Correção: Sempre drene, cancele ou descarte explicitamente.
- Bloqueio em adaptadores de stream - Cláusulas
mapnão devem bloquear. Correção:thencom offload assíncrono. - Mistura de crates de Stream - Importe
StreamExtdo crate que corresponde ao seu tipo de stream. Correção:use tokio_stream::StreamExtvsfutures::StreamExt(habilite recursos). - Sem
for awaitem Rust estável - Usewhile let SomeouStreamExt::next. Correção: Loop com.awaitemnext().
Alternativas
| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
Vec + loop | Pequenos lotes finitos | Eventos ao vivo ilimitados |
| Apenas canais | Coordenação de tarefas | Você precisa de adaptadores estilo iterador |
| Loop de polling + sleep | Tick simples de cronômetro | Processamento de eventos de alta vazão |
| API de callback | Ponte FFI C | Serviços assíncronos idiomáticos de Rust |
FAQs
Stream vs Iterator?
Iterator é pull síncrono. Stream é pull assíncrono - cada item pode esperar por I/O.
Qual StreamExt?
tokio_stream::StreamExt para streams Tokio; futures::StreamExt é mais amplo - importe um de forma consistente.
Corpos de streaming Axum?
Axum pode retornar Body::from_stream envolvendo um Stream<Item = Result<Bytes, Error>>.
Streams sqlx?
sqlx::query(...).fetch(&pool) retorna um stream de linhas - processe com try_next().await.
Mesclar múltiplos streams?
select! em streams, StreamExt::merge, ou futures::stream::select para mesclagem justa.
Backpressure?
Canais limitados bloqueiam produtores; buffer_unordered limita mapeamentos assíncronos em andamento.
Tratamento de erros?
TryStream com try_next().await ou map_err + ? dentro de try_stream!.
Testando streams?
stream::iter(vec![...]) para entradas determinísticas; timeout em testes para capturar travamentos.
pin_project?
Streams avançados usam pin_project para projetar campos de struct em poll_next.
WebSocket?
Mensagens tokio-tungstenite formam um stream de frames - mapeie para eventos do aplicativo.
Relacionados
- Noções Básicas de Async - introdução à iteração assíncrona
- Combinando Futures - select com streams
- Canais em Tokio - ReceiverStream
- I/O Assíncrono - streams de bytes
- Cancelamento e Timeouts - timeouts de stream
Versões da Pilha: Esta página foi escrita para Rust 1.97.0 (edição 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, e Polars 0.46+.