---
title: "std::error::Error Trait em Rust"
url: "https://rustlang.com.br/stdlib/error-trait/"
markdown_url: "https://rustlang.com.br/stdlib/error-trait.MD"
description: "Guia completo sobre o trait Error em Rust: tipos de erro customizados, cadeia source(), Display, downcasting e integração com thiserror."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# std::error::Error Trait em Rust

Guia completo sobre o trait Error em Rust: tipos de erro customizados, cadeia source(), Display, downcasting e integração com thiserror.


## O que é o Trait Error?

O trait `std::error::Error` é a base do sistema de tratamento de erros em Rust. Ele define a interface que todo tipo de erro deve seguir, permitindo que erros sejam compostos, encadeados e inspecionados de forma uniforme.

Qualquer tipo que implementa `Error` pode:
- Ser usado como `Box<dyn Error>` para erros genéricos
- Encadear erros com `source()` para rastrear a causa raiz
- Ser formatado para o usuário via `Display`
- Ser formatado para depuração via `Debug`
- Ser convertido via downcasting para o tipo concreto

O trait exige que o tipo implemente tanto `Debug` quanto `Display`, garantindo que todo erro tem uma representação textual.

---

## Definição do Trait

```rust
// std::error::Error
pub trait Error: Debug + Display {
    // Retorna a causa/fonte deste erro (se houver)
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        None
    }
}
```

O trait tem uma implementação padrão para `source()` que retorna `None`. Você sobrescreve quando seu erro encapsula outro erro.

### Requisitos

Para implementar `Error`, seu tipo **deve** implementar:
1. `std::fmt::Debug` — representação para desenvolvedores
2. `std::fmt::Display` — mensagem legível para o usuário

---

## Como Implementar

### Implementação manual completa

```rust
use std::fmt;
use std::error::Error;

#[derive(Debug)]
enum AppErro {
    NaoEncontrado { recurso: String },
    SemPermissao { acao: String },
    Interno(String),
}

impl fmt::Display for AppErro {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppErro::NaoEncontrado { recurso } => {
                write!(f, "Recurso não encontrado: {}", recurso)
            }
            AppErro::SemPermissao { acao } => {
                write!(f, "Sem permissão para: {}", acao)
            }
            AppErro::Interno(msg) => {
                write!(f, "Erro interno: {}", msg)
            }
        }
    }
}

impl Error for AppErro {}  // source() retorna None por padrão

fn buscar_usuario(id: u32) -> Result<String, AppErro> {
    if id == 0 {
        Err(AppErro::NaoEncontrado {
            recurso: format!("Usuário #{}", id),
        })
    } else {
        Ok(format!("Usuário #{}", id))
    }
}

fn main() {
    match buscar_usuario(0) {
        Ok(u) => println!("Encontrado: {}", u),
        Err(e) => {
            eprintln!("Erro: {}", e);
            eprintln!("Debug: {:?}", e);
        }
    }
}
```

### Implementação com cadeia de erros (source)

```rust
use std::fmt;
use std::error::Error;
use std::num::ParseIntError;
use std::io;

#[derive(Debug)]
enum ConfigErro {
    ArquivoNaoEncontrado(io::Error),
    ValorInvalido {
        campo: String,
        causa: ParseIntError,
    },
    CampoAusente(String),
}

impl fmt::Display for ConfigErro {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ConfigErro::ArquivoNaoEncontrado(_) => {
                write!(f, "Arquivo de configuração não encontrado")
            }
            ConfigErro::ValorInvalido { campo, .. } => {
                write!(f, "Valor inválido para o campo '{}'", campo)
            }
            ConfigErro::CampoAusente(campo) => {
                write!(f, "Campo obrigatório ausente: '{}'", campo)
            }
        }
    }
}

impl Error for ConfigErro {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            ConfigErro::ArquivoNaoEncontrado(e) => Some(e),
            ConfigErro::ValorInvalido { causa, .. } => Some(causa),
            ConfigErro::CampoAusente(_) => None,
        }
    }
}

// Conversões com From para usar com ?
impl From<io::Error> for ConfigErro {
    fn from(e: io::Error) -> Self {
        ConfigErro::ArquivoNaoEncontrado(e)
    }
}

fn carregar_config(caminho: &str) -> Result<u32, ConfigErro> {
    let conteudo = std::fs::read_to_string(caminho)?;

    let porta: u32 = conteudo.trim().parse().map_err(|e| {
        ConfigErro::ValorInvalido {
            campo: String::from("porta"),
            causa: e,
        }
    })?;

    Ok(porta)
}

fn imprimir_cadeia_erros(erro: &dyn Error) {
    eprintln!("Erro: {}", erro);
    let mut fonte = erro.source();
    while let Some(e) = fonte {
        eprintln!("  Causado por: {}", e);
        fonte = e.source();
    }
}

fn main() {
    match carregar_config("config.txt") {
        Ok(porta) => println!("Porta: {}", porta),
        Err(e) => imprimir_cadeia_erros(&e),
    }
    // Saída possível:
    // Erro: Arquivo de configuração não encontrado
    //   Causado por: No such file or directory (os error 2)
}
```

---

## Exemplos Práticos

### Exemplo 1: Box<dyn Error> para erros genéricos

```rust
use std::error::Error;
use std::fs;

// Box<dyn Error> aceita qualquer tipo que implemente Error
fn processar(caminho: &str) -> Result<i32, Box<dyn Error>> {
    let conteudo = fs::read_to_string(caminho)?;  // io::Error
    let numero: i32 = conteudo.trim().parse()?;    // ParseIntError
    Ok(numero * 2)
}

fn main() {
    match processar("dados.txt") {
        Ok(resultado) => println!("Resultado: {}", resultado),
        Err(e) => {
            eprintln!("Erro: {}", e);
            // Percorrer a cadeia de erros
            let mut fonte = e.source();
            while let Some(causa) = fonte {
                eprintln!("  Causado por: {}", causa);
                fonte = causa.source();
            }
        }
    }
}
```

### Exemplo 2: Downcasting de erros

```rust
use std::error::Error;
use std::io;

fn operacao() -> Result<(), Box<dyn Error>> {
    let _arquivo = std::fs::File::open("inexistente.txt")?;
    Ok(())
}

fn main() {
    if let Err(erro) = operacao() {
        // Tenta fazer downcast para io::Error
        if let Some(io_erro) = erro.downcast_ref::<io::Error>() {
            match io_erro.kind() {
                io::ErrorKind::NotFound => {
                    eprintln!("Arquivo não encontrado!");
                }
                io::ErrorKind::PermissionDenied => {
                    eprintln!("Sem permissão!");
                }
                _ => {
                    eprintln!("Erro de I/O: {}", io_erro);
                }
            }
        } else {
            eprintln!("Erro desconhecido: {}", erro);
        }
    }
}
```

### Exemplo 3: Tipo de erro com contexto

```rust
use std::error::Error;
use std::fmt;
use std::io;

#[derive(Debug)]
struct ErroComContexto {
    mensagem: String,
    fonte: Box<dyn Error + 'static>,
}

impl ErroComContexto {
    fn novo(mensagem: impl Into<String>, fonte: impl Error + 'static) -> Self {
        ErroComContexto {
            mensagem: mensagem.into(),
            fonte: Box::new(fonte),
        }
    }
}

impl fmt::Display for ErroComContexto {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.mensagem)
    }
}

impl Error for ErroComContexto {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        Some(&*self.fonte)
    }
}

// Trait de extensão para adicionar contexto a qualquer Result
trait ResultExt<T> {
    fn contexto(self, msg: impl Into<String>) -> Result<T, ErroComContexto>;
}

impl<T, E: Error + 'static> ResultExt<T> for Result<T, E> {
    fn contexto(self, msg: impl Into<String>) -> Result<T, ErroComContexto> {
        self.map_err(|e| ErroComContexto::novo(msg, e))
    }
}

fn ler_config() -> Result<String, ErroComContexto> {
    std::fs::read_to_string("/etc/app/config.toml")
        .contexto("Falha ao ler arquivo de configuração")
}

fn main() {
    match ler_config() {
        Ok(config) => println!("Config: {}", config),
        Err(e) => {
            eprintln!("Erro: {}", e);
            if let Some(fonte) = e.source() {
                eprintln!("  Causa: {}", fonte);
            }
        }
    }
}
```

### Exemplo 4: Integração com thiserror

O crate `thiserror` automatiza a implementação de `Error`, `Display` e `From`:

```rust
// No Cargo.toml: thiserror = "2"

// use thiserror::Error;
//
// #[derive(Debug, Error)]
// enum ServicoErro {
//     #[error("Usuário não encontrado: {id}")]
//     UsuarioNaoEncontrado { id: u64 },
//
//     #[error("Falha na conexão com o banco")]
//     Banco(#[from] io::Error),
//
//     #[error("Token expirado")]
//     TokenExpirado,
//
//     #[error("Valor inválido: {0}")]
//     Validacao(String),
//
//     #[error("Erro ao processar dados")]
//     Processamento {
//         #[source]
//         causa: serde_json::Error,
//     },
// }

// O código acima é equivalente a implementar manualmente:
use std::error::Error;
use std::fmt;
use std::io;

#[derive(Debug)]
enum ServicoErro {
    UsuarioNaoEncontrado { id: u64 },
    Banco(io::Error),
    TokenExpirado,
    Validacao(String),
}

impl fmt::Display for ServicoErro {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ServicoErro::UsuarioNaoEncontrado { id } => {
                write!(f, "Usuário não encontrado: {}", id)
            }
            ServicoErro::Banco(_) => write!(f, "Falha na conexão com o banco"),
            ServicoErro::TokenExpirado => write!(f, "Token expirado"),
            ServicoErro::Validacao(msg) => write!(f, "Valor inválido: {}", msg),
        }
    }
}

impl Error for ServicoErro {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            ServicoErro::Banco(e) => Some(e),
            _ => None,
        }
    }
}

impl From<io::Error> for ServicoErro {
    fn from(e: io::Error) -> Self {
        ServicoErro::Banco(e)
    }
}

fn main() {
    let erro = ServicoErro::UsuarioNaoEncontrado { id: 42 };
    eprintln!("{}", erro); // Usuário não encontrado: 42
}
```

### Exemplo 5: Percorrendo a cadeia completa de erros

```rust
use std::error::Error;

fn imprimir_cadeia_completa(erro: &dyn Error) {
    println!("Erro: {}", erro);

    let mut nivel = 1;
    let mut atual = erro.source();

    while let Some(causa) = atual {
        println!("{}Causa (nível {}): {}", "  ".repeat(nivel), nivel, causa);
        atual = causa.source();
        nivel += 1;
    }
}

fn coletar_mensagens(erro: &dyn Error) -> Vec<String> {
    let mut mensagens = vec![erro.to_string()];
    let mut atual = erro.source();
    while let Some(causa) = atual {
        mensagens.push(causa.to_string());
        atual = causa.source();
    }
    mensagens
}

fn main() {
    // Simulando um erro encadeado
    let io_err = std::io::Error::new(
        std::io::ErrorKind::ConnectionRefused,
        "conexão recusada pelo servidor"
    );

    let mensagens = coletar_mensagens(&io_err);
    for (i, msg) in mensagens.iter().enumerate() {
        println!("[{}] {}", i, msg);
    }
}
```

---

## Padrões e Boas Práticas

1. **Crie enums de erro por módulo/crate**: Cada módulo deveria ter seu próprio enum de erro que cobre todos os casos de falha possíveis.

2. **Sempre implemente `source()`**: Quando seu erro encapsula outro, retorne-o em `source()`. Isso permite rastrear a causa raiz.

3. **Display para o usuário, Debug para o desenvolvedor**: A mensagem de `Display` deve ser clara e orientada ao problema. `Debug` (via derive) mostra a estrutura interna.

4. **Use `From` para conversão automática**: Implemente `From<SubErro> for MeuErro` para que o operador `?` converta erros automaticamente. Veja [From e Into](/stdlib/from-into/).

5. **Considere `thiserror` para bibliotecas**: O crate `thiserror` elimina boilerplate sem custo em runtime. Para aplicações, `anyhow` oferece ergonomia adicional.

6. **`Box<dyn Error>` para protótipos**: Em código exploratório ou exemplos, `Result<T, Box<dyn Error>>` é prático. Para produção, prefira tipos de erro concretos.

7. **Não exponha erros internos**: Se você está criando uma biblioteca, não exponha `io::Error` ou `serde_json::Error` diretamente na sua API pública. Encapsule-os no seu tipo de erro.

---

## Veja Também

- [Display e Debug](/stdlib/display-debug/) — pré-requisitos do trait Error
- [From e Into](/stdlib/from-into/) — conversão de erros com operador `?`
- [Tratamento de Erros](/tutoriais/tratamento-de-erros/) — tutorial completo sobre erros em Rust
- [Boas Práticas de Error Handling](/artigos/boas-praticas-error-handling/) — padrões avançados de tratamento de erros
