---
title: "Como Tratar Erros em Rust (Result e Option)"
url: "https://rustlang.com.br/receitas/tratar-erros/"
markdown_url: "https://rustlang.com.br/receitas/tratar-erros.MD"
description: "Receita prática de tratamento de erros em Rust: match, operador ?, unwrap_or, map_err e erros customizados. Código completo e executável."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Como Tratar Erros em Rust (Result e Option)

Receita prática de tratamento de erros em Rust: match, operador ?, unwrap_or, map_err e erros customizados. Código completo e executável.


Tratar erros corretamente é essencial para escrever software confiável. Rust usa os tipos `Result<T, E>` e `Option<T>` em vez de exceções, forçando você a lidar com erros de forma explícita. Nesta receita, você vai aprender os padrões mais comuns de tratamento de erros com código prático e executável.

## Dependências

Nenhuma crate externa necessária:

```toml
[package]
name = "receita-erros"
version = "0.1.0"
edition = "2021"
```

## Código Completo

```rust
use std::collections::HashMap;
use std::fmt;
use std::num::ParseIntError;

// =============================================
// Erro customizado
// =============================================
#[derive(Debug)]
enum AppErro {
    NaoEncontrado(String),
    Validacao(String),
    Parse(ParseIntError),
}

impl fmt::Display for AppErro {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppErro::NaoEncontrado(msg) => write!(f, "Não encontrado: {}", msg),
            AppErro::Validacao(msg) => write!(f, "Validação falhou: {}", msg),
            AppErro::Parse(e) => write!(f, "Erro de conversão: {}", e),
        }
    }
}

impl From<ParseIntError> for AppErro {
    fn from(e: ParseIntError) -> Self {
        AppErro::Parse(e)
    }
}

// =============================================
// Funções que retornam Result
// =============================================
fn buscar_usuario(id: u32) -> Result<String, AppErro> {
    let usuarios: HashMap<u32, &str> = HashMap::from([
        (1, "Ana"),
        (2, "Bia"),
        (3, "Carlos"),
    ]);

    usuarios
        .get(&id)
        .map(|nome| nome.to_string())
        .ok_or_else(|| AppErro::NaoEncontrado(format!("Usuário ID {}", id)))
}

fn validar_idade(texto: &str) -> Result<u32, AppErro> {
    let idade: u32 = texto.trim().parse()?; // converte ParseIntError via From

    if idade < 18 {
        return Err(AppErro::Validacao(
            format!("Idade {} é menor que 18", idade),
        ));
    }
    if idade > 130 {
        return Err(AppErro::Validacao(
            format!("Idade {} é inválida", idade),
        ));
    }

    Ok(idade)
}

fn processar_cadastro(id: u32, idade_texto: &str) -> Result<String, AppErro> {
    let nome = buscar_usuario(id)?;
    let idade = validar_idade(idade_texto)?;
    Ok(format!("{} ({} anos) cadastrado com sucesso", nome, idade))
}

// =============================================
// Funções que retornam Option
// =============================================
fn encontrar_extensao(arquivo: &str) -> Option<&str> {
    let ponto = arquivo.rfind('.')?;
    Some(&arquivo[ponto + 1..])
}

fn main() {
    println!("=== 1. Match com Result ===");
    match buscar_usuario(1) {
        Ok(nome) => println!("Encontrado: {}", nome),
        Err(e) => println!("Erro: {}", e),
    }
    match buscar_usuario(99) {
        Ok(nome) => println!("Encontrado: {}", nome),
        Err(e) => println!("Erro: {}", e),
    }

    println!("\n=== 2. Match com Option ===");
    let arquivos = vec!["foto.jpg", "documento.pdf", "README", "app.rs"];
    for arquivo in &arquivos {
        match encontrar_extensao(arquivo) {
            Some(ext) => println!("{} -> extensão: {}", arquivo, ext),
            None => println!("{} -> sem extensão", arquivo),
        }
    }

    println!("\n=== 3. Operador ? (propagação) ===");
    let testes = vec![
        (1, "25"),
        (2, "15"),
        (99, "30"),
        (3, "abc"),
    ];
    for (id, idade) in &testes {
        match processar_cadastro(*id, idade) {
            Ok(msg) => println!("OK: {}", msg),
            Err(e) => println!("ERRO: {}", e),
        }
    }

    println!("\n=== 4. unwrap_or e unwrap_or_else ===");
    let nome = buscar_usuario(99).unwrap_or("Desconhecido".to_string());
    println!("unwrap_or: {}", nome);

    let nome = buscar_usuario(99).unwrap_or_else(|e| {
        println!("  (tratando erro: {})", e);
        "Anônimo".to_string()
    });
    println!("unwrap_or_else: {}", nome);

    // unwrap_or_default usa o Default do tipo
    let valor: Result<i32, &str> = Err("falhou");
    println!("unwrap_or_default: {}", valor.unwrap_or_default()); // 0

    println!("\n=== 5. map e map_err ===");
    // map transforma o valor Ok
    let resultado = validar_idade("25").map(|idade| idade * 12);
    println!("Idade em meses: {:?}", resultado);

    // map_err transforma o erro
    let resultado = validar_idade("abc")
        .map_err(|e| format!("PROBLEMA: {}", e));
    println!("map_err: {:?}", resultado);

    // and_then encadeia operações que retornam Result
    let resultado = validar_idade("25")
        .and_then(|idade| {
            if idade >= 21 {
                Ok(format!("{} anos - pode tudo!", idade))
            } else {
                Err(AppErro::Validacao("Menor de 21".into()))
            }
        });
    println!("and_then: {:?}", resultado);

    println!("\n=== 6. Convertendo Option <-> Result ===");
    // Option -> Result com ok_or
    let opt: Option<i32> = Some(42);
    let res: Result<i32, &str> = opt.ok_or("valor ausente");
    println!("Option -> Result: {:?}", res);

    let opt: Option<i32> = None;
    let res: Result<i32, &str> = opt.ok_or("valor ausente");
    println!("None -> Result:   {:?}", res);

    // Result -> Option com ok()
    let res: Result<i32, &str> = Ok(42);
    let opt: Option<i32> = res.ok();
    println!("Result -> Option: {:?}", opt);

    println!("\n=== 7. Coletando Results de iterador ===");
    let textos = vec!["1", "2", "3", "4"];
    let numeros: Result<Vec<i32>, _> = textos
        .iter()
        .map(|t| t.parse::<i32>())
        .collect();
    println!("Todos válidos: {:?}", numeros);

    let textos = vec!["1", "abc", "3"];
    let numeros: Result<Vec<i32>, _> = textos
        .iter()
        .map(|t| t.parse::<i32>())
        .collect();
    println!("Com inválido:  {:?}", numeros);

    println!("\n=== 8. if let e let else ===");
    // if let — executar código apenas se Ok/Some
    if let Ok(nome) = buscar_usuario(1) {
        println!("if let: Olá, {}!", nome);
    }

    // let else — retornar cedo se Err/None
    fn saudar(id: u32) {
        let Ok(nome) = buscar_usuario(id) else {
            println!("let else: Usuário {} não encontrado", id);
            return;
        };
        println!("let else: Bem-vindo, {}!", nome);
    }
    saudar(2);
    saudar(99);
}
```

## Saída do Programa

```
=== 1. Match com Result ===
Encontrado: Ana
Erro: Não encontrado: Usuário ID 99

=== 2. Match com Option ===
foto.jpg -> extensão: jpg
documento.pdf -> extensão: pdf
README -> sem extensão
app.rs -> extensão: rs

=== 3. Operador ? (propagação) ===
OK: Ana (25 anos) cadastrado com sucesso
ERRO: Validação falhou: Idade 15 é menor que 18
ERRO: Não encontrado: Usuário ID 99
ERRO: Erro de conversão: invalid digit found in string

=== 4. unwrap_or e unwrap_or_else ===
unwrap_or: Desconhecido
  (tratando erro: Não encontrado: Usuário ID 99)
unwrap_or_else: Anônimo
unwrap_or_default: 0

=== 5. map e map_err ===
Idade em meses: Ok(300)
map_err: Err("PROBLEMA: Erro de conversão: invalid digit found in string")
and_then: Ok("25 anos - pode tudo!")

=== 6. Convertendo Option <-> Result ===
Option -> Result: Ok(42)
None -> Result:   Err("valor ausente")
Result -> Option: Some(42)

=== 7. Coletando Results de iterador ===
Todos válidos: Ok([1, 2, 3, 4])
Com inválido:  Err(ParseIntError { kind: InvalidDigit })

=== 8. if let e let else ===
if let: Olá, Ana!
let else: Bem-vindo, Bia!
let else: Usuário 99 não encontrado
```

## Resumo dos Padrões

| Padrão | Quando usar |
|--------|-------------|
| `match` | Tratar cada caso explicitamente |
| `?` | Propagar erro para a função chamadora |
| `unwrap_or(v)` | Fornecer valor padrão fixo |
| `unwrap_or_else(f)` | Calcular valor padrão com closure |
| `map(f)` | Transformar o valor Ok/Some |
| `map_err(f)` | Transformar o tipo de erro |
| `and_then(f)` | Encadear operações que podem falhar |
| `if let` | Executar código apenas se sucesso |
| `let else` | Retornar cedo se falha |

## Veja Também

- [Tutorial: Tratamento de Erros em Rust](/tutoriais/tratamento-de-erros/) --- tutorial completo com thiserror e anyhow
- [Converter Entre Tipos de Erro](/receitas/converter-erros/) --- From trait, thiserror e anyhow
- [Rust Cheatsheet](/cheatsheet/) --- referência rápida de Result e Option
- [Glossário: Result](/glossario/) --- definição e exemplos de Result
- [Glossário: Option](/glossario/) --- definição e exemplos de Option
- [Tratamento de erros em Go](https://golang.com.br) --- compare com a abordagem de tratamento de erros em Go
