Programação Reativa em Rust: bibliotecas, exemplos e prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Programação Reativa em Rust: de objetos observáveis a fluxos assíncronos



Programação reativa é um paradigma focado no processamento de fluxos de dados assíncronos e na propagação de mudanças. No ecossistema Rust, ela é implementada através de uma combinação de um poderoso sistema de tipos, gerenciamento seguro de memória e primitivas assíncronas modernas. Neste artigo, exploraremos as principais bibliotecas, padrões e exemplos práticos de programação reativa em Rust.



Principais bibliotecas para programação reativa em Rust



Não existe um "padrão" único de programação reativa em Rust, mas existem várias bibliotecas maduras que cobrem diferentes níveis de abstração:



  • RxRust — uma adaptação da popular ReactiveX (Rx) para Rust. Fornece Observable, Observer, Scheduler e um amplo conjunto de operadores (map, filter, flat_map, merge, etc.).
  • futures-rs — biblioteca fundamental para trabalhar com computações assíncronas. O trait Stream é a base dos fluxos reativos.
  • async-std e tokio — runtimes assíncronos que fornecem canais (channels), temporizadores e outros primitivos para cadeias reativas.
  • frunk — biblioteca para programação funcional, incluindo padrões reativos como Lens e State.


Exemplo 1: Observable simples com RxRust



RxRust implementa o padrão "observador": um Observable gera eventos e um Observer se inscreve neles.



// Cargo.toml: rxrust = "1.0"use rxrust::prelude::*;

fn main() { let observable = observable::from_iter(vec![1, 2, 3, 4, 5]);

observable .filter(|x| x % 2 == 0) .map(|x| x * 10) .subscribe(|x| println!("Recebido: {}", x));}


Neste exemplo, um fluxo é criado a partir dos números 1..5, os pares são filtrados, multiplicados por 10 e exibidos. A cadeia de operadores é executada de forma lazy — somente após a chamada de subscribe.



Exemplo 2: Fluxos assíncronos com futures-rs



A biblioteca futures-rs fornece o trait Stream, que é o equivalente assíncrono de Iterator. Os combinadores de StreamExt permitem construir cadeias reativas.



// Cargo.toml: futures = "0.3"use futures::stream::{self, StreamExt};

#[tokio::main]async fn main() { let stream = stream::iter(0..10) .filter(|x| futures::future::ready(x % 2 == 0)) .map(|x| x * 10) .take(3);

let result: Vec<i32> = stream.collect().await; println!("Resultado: {:?}", result); // [0, 20, 40]}


Aqui, filter aceita uma função assíncrona, permitindo incorporar chamadas a APIs externas ou bancos de dados dentro da cadeia. take(3) limita a quantidade de elementos — este é um exemplo de "contrapressão" (backpressure).



Exemplo 3: Canais reativos com async-std



Canais são uma forma clássica de implementar interações reativas entre componentes. O async-std fornece canais multiprodutores e multiconsumidores.



// Cargo.toml: async-std = "1.12"use async_std::channel;use async_std::task;

#[async_std::main]async fn main() { let (sender, receiver) = channel::unbounded();

// Produtor task::spawn(async move { for i in 0..5 { sender.send(i).await.unwrap(); task::sleep(std::time::Duration::from_millis(100)).await; } });

// Consumidor (reativo) while let Some(msg) = receiver.recv().await { println!("Recebido: {}", msg); if msg == 4 { break; } }}


Canais são ideais para implementar um Event Bus, barramento de eventos ou sistemas reativos simples com múltiplas fontes de dados.



Padrões avançados: combinando fluxos

Recommendations