Rust Admin

O que Todo Dev Deve Saber sobre Biblioteca anyhow: Tratamento de Erros em Aplicações Rust Já leu

Por Que Tratamento de Erros em Rust é Diferente 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 . A biblioteca 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. 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. Diferente das exceções tradicionais, com 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. Fundamentos: Result, Error Traits e anyhow Entendendo o tipo Result é o coração do tratamento de erros em Rust. Ele força o programador a reconhecer que uma operação pode falhar: O

Por Que Tratamento de Erros em Rust é Diferente

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 Result<T, E>. A biblioteca anyhow 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. anyhow 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.

Diferente das exceções tradicionais, com anyhow 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.

Fundamentos: Result, Error Traits e anyhow

Entendendo o tipo Result

Result<T, E> é o coração do tratamento de erros em Rust. Ele força o programador a reconhecer que uma operação pode falhar:

use std::fs;

fn ler_arquivo(caminho: &str) -> Result<String, std::io::Error> {
    fs::read_to_string(caminho)
}

O problema: quando você mistura operações que retornam diferentes tipos de erro (io::Error, json::Error, ParseIntError), fica complexo gerenciar tudo num único tipo E.

Introduzindo anyhow

anyhow fornece o tipo Result<T, anyhow::Error>, que encapsula qualquer erro que implemente o trait std::error::Error. Adicione ao seu Cargo.toml:

[dependencies]
anyhow = "1.0"

Agora você pode fazer isto:

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),
    }
}

O operador ? faz a conversão automática usando o trait From<E> for anyhow::Error. Você não precisa definir conversões entre tipos de erro — anyhow cuida disso.

Padrões Práticos: Contexto, Logging e Debugging

Adicionando Contexto aos Erros

Erros genéricos perdem contexto. anyhow oferece .context() para adicionar informação útil:

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)
}

Quando o erro se propaga até o main, você vê uma corrente legível de "Por que falhou?" em vez de apenas "arquivo não encontrado".

Convertendo Erros Customizados

Se você já tem tipos de erro customizados, anyhow integra facilmente:

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))
}

Você pode converter qualquer erro que implemente std::error::Error para anyhow::Error com anyhow!().

Debugging com Chain de Erros

anyhow permite inspecionar toda a cadeia de causas do erro:

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);
        }
    }
}

Casos de Uso Avançados: CLI e Servidores Web

Aplicações de Linha de Comando

Em CLIs, você quer erros legíveis para o usuário final:

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(())
}

Em Frameworks Web (Actix/Axum)

Muitos frameworks suportam anyhow::Result nativamente:

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))
}

O framework converte anyhow::Error para uma resposta HTTP apropriada.

Conclusão

Três aprendizados fundamentais: Primeiro, anyhow 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. Segundo, .context() é seu aliado para transformar erros genéricos em mensagens úteis, economizando horas de debugging. Terceiro, para aplicações reais (CLIs, APIs, scripts), anyhow::Result é a escolha padrão; reserve tipos customizados para bibliotecas reutilizáveis onde o consumidor precisa diferenciar tipos de erro.

Comece pequeno: substitua unwrap() por ? com anyhow::Result e adicione .context() conforme necessário. Você verá erros mais claros imediatamente.

Referências


Artigos relacionados