---
title: "Result\u003cT,E\u003e em Rust: Ok e Err"
url: "https://rustlang.com.br/stdlib/result/"
markdown_url: "https://rustlang.com.br/stdlib/result.MD"
description: "Guia completo de Result\u003cT,E\u003e em Rust: propagação de erros com ?, map, map_err, tipos de erro customizados e padrões práticos."
date: "2026-02-23"
author: "Equipe Rust Brasil"
---

# Result<T,E> em Rust: Ok e Err

Guia completo de Result<T,E> em Rust: propagação de erros com ?, map, map_err, tipos de erro customizados e padrões práticos.


## O que é Result\<T,E\>

`Result<T, E>` é o tipo que Rust usa para representar operações que podem falhar de forma recuperável. Ele tem duas variantes: `Ok(T)` para o caso de sucesso e `Err(E)` para o caso de erro. Diferente de exceções em outras linguagens, o `Result` torna os erros **explícitos no tipo de retorno** — o compilador obriga você a lidar com eles.

Use `Result` sempre que uma operação pode falhar por motivos previsíveis: leitura de arquivos, parsing de dados, requisições de rede, validação de entrada do usuário. Para erros que indicam bugs no programa (índice fora dos limites, estado impossível), Rust usa `panic!`. Para valores opcionais sem erro, use [`Option<T>`](/stdlib/option/).

---

## Criando e Usando Result

```rust
use std::num::ParseIntError;

fn dividir(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err("Divisão por zero".to_string())
    } else {
        Ok(a / b)
    }
}

fn main() {
    // Pattern matching — forma mais completa
    match dividir(10.0, 3.0) {
        Ok(resultado) => println!("Resultado: {resultado:.2}"),
        Err(erro) => println!("Erro: {erro}"),
    }

    // if let — quando só interessa um caso
    if let Err(e) = dividir(10.0, 0.0) {
        eprintln!("Erro: {e}");
    }

    // let-else para early return
    let Ok(valor) = dividir(10.0, 2.0) else {
        eprintln!("Falhou!");
        return;
    };
    println!("Valor: {valor}");

    // parse() retorna Result
    let numero: Result<i32, ParseIntError> = "42".parse();
    println!("{numero:?}"); // Ok(42)

    let invalido: Result<i32, ParseIntError> = "abc".parse();
    println!("{invalido:?}"); // Err(invalid digit found in string)
}
```

---

## Tabela de Métodos Principais

| Método | Descrição | Exemplo |
|---|---|---|
| `is_ok()` | Retorna `true` se é `Ok` | `Ok(1).is_ok()` → `true` |
| `is_err()` | Retorna `true` se é `Err` | `Err("x").is_err()` → `true` |
| `ok()` | Converte para `Option<T>` | `Ok(1).ok()` → `Some(1)` |
| `err()` | Converte para `Option<E>` | `Err("x").err()` → `Some("x")` |
| `unwrap()` | Extrai o valor ou **panic** | `Ok(1).unwrap()` → `1` |
| `unwrap_err()` | Extrai o erro ou **panic** | `Err("x").unwrap_err()` → `"x"` |
| `unwrap_or(default)` | Extrai ou retorna default | `Err("x").unwrap_or(0)` → `0` |
| `unwrap_or_else(f)` | Extrai ou calcula via closure | `Err("x").unwrap_or_else(\|_\| 0)` |
| `unwrap_or_default()` | Extrai ou usa `Default` | `Err("x").unwrap_or_default()` → `0` |
| `expect(msg)` | Como `unwrap` com mensagem | `res.expect("falhou")` |
| `map(f)` | Transforma o valor Ok | `Ok(1).map(\|x\| x * 2)` → `Ok(2)` |
| `map_err(f)` | Transforma o valor Err | `Err(1).map_err(\|e\| e + 1)` → `Err(2)` |
| `and_then(f)` | Encadeia operações Result | `Ok(1).and_then(\|x\| Ok(x + 1))` |
| `or_else(f)` | Alternativa se for Err | `Err(1).or_else(\|_\| Ok(0))` |
| `as_ref()` | `Result<&T, &E>` a partir de `&Result` | `res.as_ref()` |
| `inspect(f)` | Executa closure sem consumir (Rust 1.76+) | `res.inspect(\|v\| println!("{v}"))` |
| `inspect_err(f)` | Inspeciona o erro sem consumir | `res.inspect_err(\|e\| log::error!("{e}"))` |

---

## Exemplos Práticos

### 1. Propagação de Erros com ?

O operador `?` é a forma idiomática de propagar erros em Rust. Ele extrai o valor de `Ok` ou retorna `Err` imediatamente da função.

```rust
use std::fs;
use std::io;
use std::num::ParseIntError;

#[derive(Debug)]
enum AppErro {
    Io(io::Error),
    Parse(ParseIntError),
}

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

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

fn ler_numero_do_arquivo(caminho: &str) -> Result<i32, AppErro> {
    let conteudo = fs::read_to_string(caminho)?; // io::Error → AppErro
    let numero = conteudo.trim().parse::<i32>()?; // ParseIntError → AppErro
    Ok(numero)
}

fn main() {
    match ler_numero_do_arquivo("numero.txt") {
        Ok(n) => println!("Número lido: {n}"),
        Err(AppErro::Io(e)) => eprintln!("Erro de I/O: {e}"),
        Err(AppErro::Parse(e)) => eprintln!("Erro de parse: {e}"),
    }
}
```

### 2. Transformando Erros com map_err

```rust
fn validar_idade(input: &str) -> Result<u8, String> {
    let idade: u8 = input
        .trim()
        .parse()
        .map_err(|e| format!("'{input}' não é uma idade válida: {e}"))?;

    if idade < 1 || idade > 150 {
        return Err(format!("Idade {idade} fora do intervalo permitido (1-150)"));
    }

    Ok(idade)
}

fn main() {
    let testes = vec!["25", "abc", "200", "0", "30"];

    for entrada in testes {
        match validar_idade(entrada) {
            Ok(idade) => println!("{entrada} → Idade válida: {idade}"),
            Err(e) => println!("{entrada} → Erro: {e}"),
        }
    }
}
```

### 3. Coletando Results em um Vec

```rust
fn main() {
    let entradas = vec!["1", "2", "3", "4", "5"];

    // collect() que falha no primeiro erro
    let todos: Result<Vec<i32>, _> = entradas
        .iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Todos: {todos:?}"); // Ok([1, 2, 3, 4, 5])

    // Com um valor inválido
    let com_erro: Result<Vec<i32>, _> = vec!["1", "abc", "3"]
        .iter()
        .map(|s| s.parse::<i32>())
        .collect();
    println!("Com erro: {com_erro:?}"); // Err(...)

    // Separando sucessos e falhas com partition
    let misturado = vec!["10", "xyz", "20", "abc", "30"];
    let (ok, erros): (Vec<_>, Vec<_>) = misturado
        .iter()
        .map(|s| s.parse::<i32>())
        .partition(Result::is_ok);

    let numeros: Vec<i32> = ok.into_iter().map(Result::unwrap).collect();
    let falhas: Vec<String> = erros
        .into_iter()
        .map(|e| e.unwrap_err().to_string())
        .collect();

    println!("Números: {numeros:?}");  // [10, 20, 30]
    println!("Erros: {falhas:?}");
}
```

### 4. Tipo de Erro Customizado com thiserror

```rust
// Usando derivação manual (sem crate externo)
use std::fmt;
use std::io;

#[derive(Debug)]
enum ConfigErro {
    ArquivoNaoEncontrado(io::Error),
    ChaveAusente(String),
    ValorInvalido { chave: String, valor: String },
}

impl fmt::Display for ConfigErro {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ConfigErro::ArquivoNaoEncontrado(e) => {
                write!(f, "Arquivo de configuração não encontrado: {e}")
            }
            ConfigErro::ChaveAusente(chave) => {
                write!(f, "Chave obrigatória ausente: '{chave}'")
            }
            ConfigErro::ValorInvalido { chave, valor } => {
                write!(f, "Valor inválido '{valor}' para a chave '{chave}'")
            }
        }
    }
}

impl std::error::Error for ConfigErro {}

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

fn carregar_porta(config: &std::collections::HashMap<String, String>) -> Result<u16, ConfigErro> {
    let valor = config
        .get("porta")
        .ok_or_else(|| ConfigErro::ChaveAusente("porta".to_string()))?;

    valor.parse::<u16>().map_err(|_| ConfigErro::ValorInvalido {
        chave: "porta".to_string(),
        valor: valor.clone(),
    })
}

fn main() {
    use std::collections::HashMap;

    let mut config = HashMap::new();
    config.insert("porta".to_string(), "8080".to_string());

    match carregar_porta(&config) {
        Ok(porta) => println!("Porta: {porta}"),
        Err(e) => eprintln!("Erro: {e}"),
    }

    // Testando com valor inválido
    config.insert("porta".to_string(), "abc".to_string());
    match carregar_porta(&config) {
        Ok(porta) => println!("Porta: {porta}"),
        Err(e) => eprintln!("Erro: {e}"),
    }
}
```

### 5. Encadeamento Fluente com and_then

```rust
fn parse_coordenada(input: &str) -> Result<(f64, f64), String> {
    let partes: Vec<&str> = input.split(',').collect();

    if partes.len() != 2 {
        return Err(format!("Esperado 'lat,lon', recebido: '{input}'"));
    }

    let lat = partes[0].trim().parse::<f64>()
        .map_err(|e| format!("Latitude inválida: {e}"))?;
    let lon = partes[1].trim().parse::<f64>()
        .map_err(|e| format!("Longitude inválida: {e}"))?;

    validar_latitude(lat)
        .and_then(|lat| validar_longitude(lon).map(|lon| (lat, lon)))
}

fn validar_latitude(lat: f64) -> Result<f64, String> {
    if (-90.0..=90.0).contains(&lat) {
        Ok(lat)
    } else {
        Err(format!("Latitude {lat} fora do intervalo [-90, 90]"))
    }
}

fn validar_longitude(lon: f64) -> Result<f64, String> {
    if (-180.0..=180.0).contains(&lon) {
        Ok(lon)
    } else {
        Err(format!("Longitude {lon} fora do intervalo [-180, 180]"))
    }
}

fn main() {
    let testes = vec![
        "-23.5505, -46.6333",   // São Paulo
        "91.0, 0.0",            // Latitude inválida
        "abc, 10.0",            // Parse error
        "0.0",                  // Formato inválido
    ];

    for entrada in testes {
        match parse_coordenada(entrada) {
            Ok((lat, lon)) => println!("({lat}, {lon}) - Válido"),
            Err(e) => println!("'{entrada}' - Erro: {e}"),
        }
    }
}
```

---

## Dicas de Performance e Armadilhas

1. **Use `?` ao invés de `match` para propagação**: O operador `?` é mais conciso e idiomático. Ele também chama `From::from` automaticamente para converter tipos de erro.

2. **Evite `unwrap()` em código de produção**: Use `expect()` no mínimo (para documentar por que o valor deveria ser Ok) ou, melhor ainda, propague o erro com `?`.

3. **`Box<dyn Error>` para prototipagem rápida**: Em `main()` ou scripts rápidos, use `Result<T, Box<dyn std::error::Error>>` para aceitar qualquer tipo de erro sem criar enums personalizados.

4. **`map_err` para contexto adicional**: Sempre que propagar um erro, considere usar `map_err` para adicionar contexto sobre o que estava sendo feito quando o erro ocorreu.

5. **Result é um iterador**: `Result` implementa `IntoIterator` — `Ok(v)` produz um iterador com um elemento, `Err` produz um iterador vazio. Isso é útil com `flat_map` e `chain`.

6. **`?` em main()**: Desde Rust 1.26, `main()` pode retornar `Result`. Basta declarar `fn main() -> Result<(), Box<dyn std::error::Error>>`.

---

## Veja Também

- [Tratamento de Erros em Rust](/tutoriais/tratamento-de-erros/) — tutorial completo
- [Tratar Erros em Rust](/receitas/tratar-erros/) — receitas práticas
- [Option\<T\>: Some e None](/stdlib/option/) — para valores opcionais sem erro
- [Iterator Trait](/stdlib/iterator/) — collect() converte iteradores de Results
- [String e &str](/stdlib/string/) — `parse()` retorna Result
- [Erros Comuns em Rust](/erros/) — soluções para mensagens de erro frequentes
- [Cheatsheet Rust](/cheatsheet/) — referência rápida de sintaxe
- [Documentação oficial — std::result::Result](https://doc.rust-lang.org/std/result/enum.Result.html)
