Programação Reativa em Rust: visão geral das ferramentas e exemplos de código
Programação reativa é um paradigma orientado a fluxos de dados assíncronos e propagação de mudanças. No ecossistema Rust, ela é implementada através de bibliotecas poderosas, como RxRust (Reactive Extensions), futures-rs e async-std. Essas ferramentas permitem construir sistemas elegantes, tolerantes a falhas e de alto desempenho, desde aplicações GUI até serviços de rede. Neste artigo, abordaremos os conceitos-chave, mostraremos exemplos práticos e ajudaremos você a escolher a biblioteca adequada para seu projeto em Rust.
Principais bibliotecas para programação reativa em Rust
As bibliotecas mais populares para programação reativa em Rust incluem:
- RxRust — porta do ReactiveX (Rx). Fornece Observable, Observer, operadores map, filter, merge e outros. Ideal para tratamento de eventos e fluxos de dados.
- futures-rs — biblioteca base para computação assíncrona. Inclui Stream, Future, Sink. Está na base de muitos sistemas reativos.
- async-std — biblioteca padrão assíncrona. Fornece versões assíncronas de E/S de arquivos, operações de rede e canais (channels) para fluxos reativos.
- tokio — runtime assíncrono com suporte a multithreading. Frequentemente usado em conjunto com futures-rs para construir aplicações de rede reativas.
Exemplo 1: Usando RxRust para tratamento de eventos
RxRust permite criar Observable a partir de iteradores, eventos ou fontes assíncronas. Abaixo, um exemplo simples de geração de números, filtragem e inscrição no fluxo:
use rxrust::prelude::*;
fn main() { // Criando um Observable a partir de um intervalo de números observable::from_iter(1..10) .filter(|x| x % 2 == 0) // Mantendo apenas os pares .map(|x| x * 10) // Multiplicando por 10 .subscribe( |x| println!("Recebido: {}", x), |err| eprintln!("Erro: {}", err), || println!("Fluxo concluído"), );}Resultado da execução:
Recebido: 20Recebido: 40Recebido: 60Recebido: 80Fluxo concluídoExemplo 2: Fluxos assíncronos com futures-rs
A biblioteca futures-rs fornece o trait Stream, que é o análogo assíncrono de Iterator. O exemplo a seguir demonstra a criação de um fluxo, sua transformação e coleta de resultados:
use futures::stream::{self, StreamExt};
#[tokio::main]async fn main() { let stream = stream::iter(1..=5) .map(|x| x * x) .filter(|x| futures::future::ready(x % 2 == 0));
let results: Vec<i32> = stream.collect().await; println!("Quadrados dos números pares: {:?}", results);}Saída:
Quadrados dos números pares: [4, 16]Exemplo 3: Canais reativos em async-std
Canais (channels) são a base dos sistemas reativos. A biblioteca async-std fornece canais multiprodutores que podem ser usados para transmitir eventos entre tarefas:
use async_std::sync::channel;use async_std::task;
fn main() { let (sender, receiver) = channel::unbounded::<String>();
// Tarefa remetente task::spawn(async move { sender.send("Olá".to_string()).await.unwrap(); sender.send("Mundo".to_string()).await.unwrap(); });
// Tarefa receptora (ouvinte reativo) task::block_on(async { while let Some(msg) = receiver.recv().await { println!("Mensagem recebida: {}", msg); } });}Este padrão está na base da arquitetura de aplicações orientadas a eventos em Rust.