<h2>Por Que Tratamento de Erros em Rust é Diferente</h2>
<p>Em Rust, erros não são exceções que explodem silenciosamente. O sistema de tipos força você a lidar com eles explicitamente através de <code>Result<T, E></code>. A biblioteca <code>anyhow</code> surge para resolver um problema real: quando você tem múltiplas fontes de erro em uma aplicação, é cansativo definir tipos de erro customizados para cada contexto. <code>anyhow</code> oferece um tipo de erro genérico e flexível que funciona como um "coringa" para tratamento de erros, permitindo que você se concentrate na lógica da sua aplicação em vez de criar boilerplate.</p>
<p>Diferente das exceções tradicionais, com <code>anyhow</code> você mantém segurança em tempo de compilação enquanto reduz complexidade. A biblioteca é ideal para aplicações, CLIs e servidores onde você quer retornar erros sem criar uma hierarquia elaborada de tipos customizados.</p>
<h2>Fundamentos: Result, Error Traits e anyhow</h2>
<h3>Entendendo o tipo Result</h3>
<p><code>Result<T, E></code> é o coração do tratamento de erros em Rust. Ele força o programador a reconhecer que uma operação pode falhar:</p>
<pre><code class="language-rust">use std::fs;
fn ler_arquivo(caminho: &str) -> Result<String, std::io::Error> {
fs::read_to_string(caminho)
}</code></pre>
<p>O problema: quando você mistura operações que retornam diferentes tipos de erro (<code>io::Error</code>, <code>json::Error</code>, <code>ParseIntError</code>), fica complexo gerenciar tudo num único tipo <code>E</code>.</p>
<h3>Introduzindo anyhow</h3>
<p><code>anyhow</code> fornece o tipo <code>Result<T, anyhow::Error></code>, que encapsula qualquer erro que implemente o trait <code>std::error::Error</code>. Adicione ao seu <code>Cargo.toml</code>:</p>
<pre><code class="language-toml">[dependencies]
anyhow = "1.0"</code></pre>
<p>Agora você pode fazer isto:</p>
<pre><code class="language-rust">use anyhow::Result;
use std::fs;
use std::num::ParseIntError;
fn processar_dados(caminho: &str) -> Result<i32> {
let conteudo = fs::read_to_string(caminho)?; // io::Error convertido automaticamente
let numero: i32 = conteudo.trim().parse()?; // ParseIntError convertido automaticamente
Ok(numero * 2)
}
fn main() {
match processar_dados("dados.txt") {
Ok(valor) => println!("Resultado: {}", valor),
Err(e) => eprintln!("Erro: {}", e),
}
}</code></pre>
<p>O operador <code>?</code> faz a conversão automática usando o trait <code>From<E> for anyhow::Error</code>. Você não precisa definir conversões entre tipos de erro — <code>anyhow</code> cuida disso.</p>
<h2>Padrões Práticos: Contexto, Logging e Debugging</h2>
<h3>Adicionando Contexto aos Erros</h3>
<p>Erros genéricos perdem contexto. <code>anyhow</code> oferece <code>.context()</code> para adicionar informação útil:</p>
<pre><code class="language-rust">use anyhow::{Context, Result};
use std::fs;
fn carregar_config(caminho: &str) -> Result<String> {
fs::read_to_string(caminho)
.context(format!("Falha ao ler arquivo de configuração: {}", caminho))?;
let config: serde_json::Value = serde_json::from_str(&conteudo)
.context("Configuração JSON inválida")?;
Ok(conteudo)
}</code></pre>
<p>Quando o erro se propaga até o <code>main</code>, você vê uma corrente legível de "Por que falhou?" em vez de apenas "arquivo não encontrado".</p>
<h3>Convertendo Erros Customizados</h3>
<p>Se você já tem tipos de erro customizados, <code>anyhow</code> integra facilmente:</p>
<pre><code class="language-rust">use anyhow::{anyhow, Result};
#[derive(Debug)]
enum MeuErro {
ConfigInvalida(String),
ConexaoPerdida,
}
impl std::fmt::Display for MeuErro {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
MeuErro::ConfigInvalida(msg) => write!(f, "Config inválida: {}", msg),
MeuErro::ConexaoPerdida => write!(f, "Conexão perdida"),
}
}
}
impl std::error::Error for MeuErro {}
fn conectar() -> Result<String> {
Err(anyhow!(MeuErro::ConexaoPerdida))
}</code></pre>
<p>Você pode converter qualquer erro que implemente <code>std::error::Error</code> para <code>anyhow::Error</code> com <code>anyhow!()</code>.</p>
<h3>Debugging com Chain de Erros</h3>
<p><code>anyhow</code> permite inspecionar toda a cadeia de causas do erro:</p>
<pre><code class="language-rust">use anyhow::Result;
fn processar_requisicao() -> Result<()> {
// Simula erro aninhado
let resultado = (|| -> Result<()> {
Err(anyhow::anyhow!("Erro interno"))
})()?;
Ok(())
}
fn main() {
if let Err(e) = processar_requisicao() {
eprintln!("Erro raiz: {}", e);
// Inspeciona chain completa
for causa in e.chain().skip(1) {
eprintln!(" Causado por: {}", causa);
}
}
}</code></pre>
<h2>Casos de Uso Avançados: CLI e Servidores Web</h2>
<h3>Aplicações de Linha de Comando</h3>
<p>Em CLIs, você quer erros legíveis para o usuário final:</p>
<pre><code class="language-rust">use anyhow::Result;
use std::env;
use std::fs;
fn main() {
if let Err(e) = executar() {
eprintln!("erro: {}", e);
std::process::exit(1);
}
}
fn executar() -> Result<()> {
let arquivo = env::args()
.nth(1)
.ok_or_else(|| anyhow::anyhow!("Use: app <arquivo>"))?;
let conteudo = fs::read_to_string(&arquivo)
.map_err(|e| anyhow::anyhow!("Não consegui ler '{}': {}", arquivo, e))?;
println!("{}", conteudo);
Ok(())
}</code></pre>
<h3>Em Frameworks Web (Actix/Axum)</h3>
<p>Muitos frameworks suportam <code>anyhow::Result</code> nativamente:</p>
<pre><code class="language-rust">use anyhow::Result;
use actix_web::{web, HttpResponse};
async fn buscar_usuario(id: web::Path<u32>) -> Result<HttpResponse> {
let dados = serde_json::json!({"id": id.into_inner(), "nome": "Alice"});
Ok(HttpResponse::Ok().json(dados))
}</code></pre>
<p>O framework converte <code>anyhow::Error</code> para uma resposta HTTP apropriada.</p>
<h2>Conclusão</h2>
<p>Três aprendizados fundamentais: <strong>Primeiro</strong>, <code>anyhow</code> elimina boilerplate de tipos de erro customizados, mantendo segurança em tempo de compilação — use quando você não precisa tratar tipos de erro diferentes de forma específica. <strong>Segundo</strong>, <code>.context()</code> é seu aliado para transformar erros genéricos em mensagens úteis, economizando horas de debugging. <strong>Terceiro</strong>, para aplicações reais (CLIs, APIs, scripts), <code>anyhow::Result</code> é a escolha padrão; reserve tipos customizados para bibliotecas reutilizáveis onde o consumidor precisa diferenciar tipos de erro.</p>
<p>Comece pequeno: substitua <code>unwrap()</code> por <code>?</code> com <code>anyhow::Result</code> e adicione <code>.context()</code> conforme necessário. Você verá erros mais claros imediatamente.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://docs.rs/anyhow/" target="_blank" rel="noopener noreferrer">Documentação Oficial do anyhow</a></li>
<li><a href="https://doc.rust-lang.org/book/ch09-00-error-handling.html" target="_blank" rel="noopener noreferrer">Rust Book - Error Handling</a></li>
<li><a href="https://doc.rust-lang.org/std/error/trait.Error.html" target="_blank" rel="noopener noreferrer">std::error::Error Trait</a></li>
<li><a href="https://blog.logrocket.com/error-handling-in-rust/" target="_blank" rel="noopener noreferrer">Artigo: Error Handling in Rust (LogRocket)</a></li>
<li><a href="https://doc.rust-lang.org/rust-by-example/error.html" target="_blank" rel="noopener noreferrer">Rust by Example - Error Handling</a></li>
</ul>