Noções Básicas de Async
9 exemplos para você começar com async em Rust - 6 básicos e 3 intermediários.
Pré-requisitos
- Rust 1.97.0 (edição 2024).
- Adicione Tokio para exemplos executáveis:
tokio = { version = "1", features = ["full"] }.
Exemplos Básicos
1. async fn e await
async fn retorna um Future que deve ser executado por um runtime.
async fn fetch_label() -> String {
"ready".to_string()
}
#[tokio::main]
async fn main() {
let label = fetch_label().await;
println!("{label}");
}async fnnão é executado até ser.awaited ou spawned..awaitcede o controle enquanto espera por I/O.#[tokio::main]fornece o executor para binários.
Relacionado: Noções Básicas de Tokio - configuração do runtime
2. Futures Preguiçosas
Chamar uma função async cria um future sem iniciar o trabalho.
async fn work() {
println!("running");
}
#[tokio::main]
async fn main() {
let _future = work(); // nada é impresso ainda
work().await; // agora ele executa
}- Futures são preguiçosos até serem "pollados".
- Descartar um future cancela o trabalho que não foi concluído.
- Armazene futures em variáveis apenas quando pretender aguardá-los mais tarde.
Relacionado: A Trait Future - poll e wakers
3. Tarefas Concorrentes com spawn
Execute trabalho concorrentemente no runtime.
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async {
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
42
});
let result = handle.await.unwrap();
println!("{result}");
}tokio::spawnagenda uma tarefa no runtime.JoinHandleé em si um future - aguarde-o para obter o resultado.- Tarefas "spawnadas" devem ser
Sendno runtime multi-thread padrão.
Relacionado: Tarefas e JoinHandles - ciclo de vida da tarefa
4. await Sequencial vs. Concorrente
Dois awaits em sequência levam mais tempo do que joins concorrentes.
async fn step(name: &str, ms: u64) {
println!("{name} start");
tokio::time::sleep(std::time::Duration::from_millis(ms)).await;
println!("{name} end");
}
#[tokio::main]
async fn main() {
step("a", 50).await;
step("b", 50).await; // ~100ms total
tokio::join!(step("c", 50), step("d", 50)); // ~50ms total
}awaitsequencial bloqueia esta tarefa até que cada etapa termine.tokio::join!executa futures concorrentemente em uma tarefa.- Use
join!ouspawnquando as etapas forem independentes.
Relacionado: Combinando Futures - join e select
5. Tipos de Retorno de async fn
Funções async retornam impl Future<Output = T>.
async fn ok() -> Result<i32, std::io::Error> {
Ok(1)
}
#[tokio::main]
async fn main() {
match ok().await {
Ok(n) => println!("{n}"),
Err(e) => eprintln!("{e}"),
}
}?funciona dentro deasync fncomo em funções síncronas.- Erros se propagam quando o future é aguardado.
- Use
anyhowouthiserrorem aplicações para ergonomia.
Relacionado: Cancelamento e Timeouts - caminhos de erro
6. Bloquear é Proibido no Executor
Nunca chame I/O bloqueante dentro de tarefas async sem descarregar.
// RUIM em async: std::thread::sleep ou std::fs::read_to_string bloqueando o worker
#[tokio::main]
async fn main() {
let data = tokio::task::spawn_blocking(|| {
std::fs::read_to_string("/etc/hosts")
})
.await
.unwrap()
.unwrap();
println!("{} bytes", data.len());
}- Bloquear "mata de fome" as threads worker do Tokio.
spawn_blockingexecuta em um pool de threads dedicado.- Prefira crates async (
tokio::fs,reqwest) quando disponíveis.
Relacionado: Bloqueando em Async - guia completo
Exemplos Intermediários
7. Padrão de Cliente Compartilhado
Abra recursos de longa duração uma vez na inicialização.
#[tokio::main]
async fn main() {
let client = reqwest::Client::new();
let resp = client
.get("https://httpbin.org/get")
.send()
.await
.unwrap();
println!("{}", resp.status());
}- Reutilize
reqwest::Cliententre requisições (pooling de conexões). - Em Axum, armazene
ClientemAppStateviaArc. - Feche recursos no desligamento em hooks de lifespan.
Relacionado: I/O Async - rede e arquivos
8. Stream de Eventos (Conceito)
Iteração async usa Stream em vez de Iterator.
use tokio_stream::{self as stream, StreamExt};
#[tokio::main]
async fn main() {
let mut stream = stream::iter(vec![1, 2, 3]);
while let Some(n) = stream.next().await {
println!("{n}");
}
}StreamExt::nexté iteração async.- Backpressure se aplica a produtores e consumidores.
- Veja a página Streams para
futures::Streamem profundidade.
Relacionado: Streams - iteração async
9. Cancelamento por Drop
Descartar um future para o trabalho em andamento em pontos de await.
async fn long_running() {
tokio::time::sleep(std::time::Duration::from_secs(60)).await;
}
#[tokio::main]
async fn main() {
let handle = tokio::spawn(long_running());
handle.abort();
let _ = handle.await; // JoinError esperado
}abortcancela tarefas "spawnadas".- Descartar um future sem aguardá-lo também cancela.
- Projete a limpeza com
Dropoutokio::select!para desligamento gracioso.
Relacionado: Desligamento Gracioso - padrões de produção
Versões da Stack: 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+.