Introdução à Programação Assíncrona em Rust
Programação assíncrona é uma abordagem que permite que sua aplicação execute várias tarefas simultaneamente de forma eficiente, sem bloquear o fluxo principal de execução. Em Rust, essa abordagem é implementada através do modelo async/await, que garante a segurança de memória e alta performance características da linguagem.
Ao contrário do multithreading tradicional, o código assíncrono opera dentro de uma ou mais threads, alternando entre tarefas durante períodos de espera (por exemplo, I/O ou requisições de rede). Isso o torna ideal para criar servidores web de alta carga, aplicações de rede e ferramentas de processamento de dados.
Neste artigo, vamos explorar os fundamentos da programação assíncrona em Rust: desde conceitos-chave até exemplos práticos. Você aprenderá como funcionam Future, async e await, conhecerá o runtime popular Tokio e escreverá seu primeiro projeto assíncrono.
Conceitos Principais: Future, async e await
O que é Future?
Em Rust, uma tarefa assíncrona é representada pela trait Future. Future é um valor que pode ainda não estar pronto, mas será obtido no futuro. Cada Future possui um método poll, que verifica se a tarefa foi concluída. Se a tarefa ainda está em execução, retorna Pending; se concluída, retorna Ready(value).
Por si só, um Future não executa nenhum trabalho. Ele apenas descreve o que deve acontecer. Para executar código assíncrono, é necessário um runtime — um executor que faz polling dos Futures e gerencia sua execução.
As palavras-chave async e await
A palavra-chave async transforma uma função comum em uma função assíncrona, que retorna um Future. Por exemplo:
async fn hello() -> String { "Olá, mundo!".to_string()}Para obter o resultado de um Future, usa-se .await. Ele pausa a execução da função atual até que o Future seja concluído:
let result = hello().await;println!("{}", result);Importante: .await só pode ser usado dentro de uma função assíncrona ou de um bloco async {}.
Ciclo de vida de uma tarefa assíncrona
- Criação do
Future(chamada de uma função assíncrona). - Entrega do
Futureao runtime para execução. - O runtime faz polling do
Futureatravés depoll. - Se a tarefa for bloqueada (aguardando I/O), o runtime alterna para outra tarefa.
- Quando o resultado estiver pronto, o
FutureretornaReady.
Ecossistema: Tokio e async-std
Para trabalhar com código assíncrono em Rust, são usados runtimes assíncronos. Os dois mais populares são Tokio e async-std.
Tokio
Tokio é o padrão industrial para programação assíncrona em Rust. Ele fornece um escalonador multi-thread, I/O assíncrono, timers, sincronização e protocolos de rede. A maioria dos grandes projetos (por exemplo, Hyper, Tonic, Axum) é construída sobre Tokio.
Para começar a trabalhar com Tokio, adicione a dependência no Cargo.toml:
[dependencies]tokio = { version = "1", features = ["full"] }A macro principal para executar código assíncrono é #[tokio::main]:
#[tokio::main]async fn main() { println!("Hello from Tokio!");}async-std
async-std é um runtime alternativo que busca ser o mais semelhante possível à biblioteca padrão do Rust. É mais simples para iniciantes, mas menos popular em produção.
Exemplo