E0728: await Usado Fora de Função Async
O erro E0728 ocorre quando você usa .await em um contexto que não é async — ou seja, fora de uma função async fn, um bloco async {} ou uma closure async. O .await só pode ser usado dentro de contextos assíncronos.
A Mensagem de Erro
error[E0728]: `await` is only allowed inside `async` functions and blocks
--> src/main.rs:5:29
|
4 | fn buscar_dados() -> String {
| ------------ this is not `async`
5 | let resposta = reqwest::get("https://api.exemplo.com").await;
| ^^^^^^ only allowed inside `async` functions and blocks
Variação em closure não-async:
error[E0728]: `await` is only allowed inside `async` functions and blocks
--> src/main.rs:3:38
|
3 | let processar = |url| { fetch(url).await };
| ----- ^^^^^^ only allowed inside `async` functions and blocks
| |
| this is not `async`
O Que Significa
No Rust, async/await é um mecanismo de programação assíncrona que permite escrever código não-bloqueante de forma legível. O .await pausa a execução da função atual até que o future (operação assíncrona) seja concluído.
Porém, .await só funciona dentro de um contexto async porque:
- Funções async são transformadas em máquinas de estado pelo compilador
- O
.awaitmarca os pontos de pausa nessa máquina de estado - Funções síncronas normais não têm essa infraestrutura
Sem o contexto async, o compilador não sabe como gerar a máquina de estado necessária para pausar e retomar a execução.
Código com Erro
Função síncrona tentando usar await
// ERRO: função não é async
fn buscar_dados() -> String {
let corpo = reqwest::get("https://api.exemplo.com")
.await // ERRO: não está em contexto async
.unwrap()
.text()
.await // ERRO: mesmo problema
.unwrap();
corpo
}
main() sem macro async
// ERRO: main normal não é async
fn main() {
let dados = buscar_dados().await; // ERRO
println!("{}", dados);
}
async fn buscar_dados() -> String {
"dados".to_string()
}
Closure não-async
async fn processar_urls(urls: Vec<String>) {
let resultados: Vec<_> = urls.iter().map(|url| {
// ERRO: closure não é async
reqwest::get(url).await
}).collect();
}
Como Resolver
Solução 1: Tornar a Função Async
Adicione async à declaração da função:
// CORRETO: função async
async fn buscar_dados() -> String {
let corpo = reqwest::get("https://api.exemplo.com")
.await
.unwrap()
.text()
.await
.unwrap();
corpo
}
Lembre-se: uma função async fn retorna um Future, não o valor diretamente. O chamador precisa usar .await (que também precisa estar em contexto async).
Solução 2: Usar Runtime Async para main()
Com tokio:
#[tokio::main]
async fn main() {
let dados = buscar_dados().await;
println!("{}", dados);
}
async fn buscar_dados() -> String {
"dados".to_string()
}
Certifique-se de ter as features corretas:
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
Com async-std:
#[async_std::main]
async fn main() {
let dados = buscar_dados().await;
println!("{}", dados);
}
Solução 3: Usar Bloco async {} dentro de Contexto Síncrono
Para executar código async em contexto síncrono, use block_on:
fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
let resultado = rt.block_on(async {
buscar_dados().await
});
println!("{}", resultado);
}
async fn buscar_dados() -> String {
"dados do servidor".to_string()
}
Ou com a versão simplificada:
fn funcao_sincrona() -> String {
// Cria runtime temporário para executar código async
tokio::runtime::Runtime::new()
.unwrap()
.block_on(buscar_dados())
}
async fn buscar_dados() -> String {
"dados".to_string()
}
Cuidado: Não use block_on dentro de um contexto que já está em um runtime async — isso causa deadlock.
Solução 4: Closures Async
Para closures, use a sintaxe async:
async fn processar_urls(urls: Vec<String>) {
// Use futures::future::join_all para processar em paralelo
let futures = urls.into_iter().map(|url| {
async move { // Closure async
let resp = reqwest::get(&url).await.unwrap();
resp.text().await.unwrap()
}
});
let resultados: Vec<String> = futures::future::join_all(futures).await;
println!("{:?}", resultados);
}
Ou processe sequencialmente com um loop:
async fn processar_sequencial(urls: Vec<String>) {
for url in &urls {
let resp = reqwest::get(url).await.unwrap();
let corpo = resp.text().await.unwrap();
println!("URL: {} -> {} bytes", url, corpo.len());
}
}
Solução 5: Traits com Métodos Async
Para traits com métodos async, use o crate async-trait ou a sintaxe nativa (estabilizada no Rust 1.75+):
// Com async-trait (compatível com versões anteriores)
use async_trait::async_trait;
#[async_trait]
trait Repositorio {
async fn buscar(&self, id: u64) -> Option<String>;
async fn salvar(&self, id: u64, valor: String) -> Result<(), String>;
}
struct BancoDeDados;
#[async_trait]
impl Repositorio for BancoDeDados {
async fn buscar(&self, id: u64) -> Option<String> {
Some(format!("Item {}", id))
}
async fn salvar(&self, id: u64, valor: String) -> Result<(), String> {
println!("Salvando {} = {}", id, valor);
Ok(())
}
}
Resumo: Onde .await Funciona
| Contexto | Pode usar .await? |
|---|---|
async fn | Sim |
async { ... } bloco | Sim |
async move { ... } | Sim |
fn normal | Nao |
Closure || { ... } | Nao |
async || { ... } | Sim |
#[tokio::main] async fn main() | Sim |
fn main() sem macro | Nao |