Introdução: filosofia de tratamento de erros em Rust
Ao contrário de muitas linguagens com exceções (Java, Python, C++), Rust não usa a construção clássica try/catch. Em vez disso, a linguagem oferece uma abordagem mais explícita e type-safe. Erros em Rust não são fluxos de controle ocultos, mas valores de retorno. Isso torna o código previsível e protege contra falhas "silenciosas".
Rust tem dois tipos principais para representar situações de erro:
- Result<T, E> — para erros recuperáveis (erros de I/O, parsing, lógica de negócios).
- Option<T> — para ausência de valor (análogo a null, mas seguro).
Neste artigo, vamos explorar como trabalhar com esses tipos, como substituí-los no mundo try/catch e como escrever código robusto em Rust.
1. Result<T, E>: a principal ferramenta de tratamento de erros
O tipo Result é um enum com duas variantes: Ok(T) (sucesso) e Err(E) (erro). Ele é usado na maioria das funções que podem falhar.
1.1. Criando e retornando Result
use std::fs::File;use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> { let mut file = File::open(path)?; // operador ? — análogo ao try! let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}Aqui, o operador ? é um elemento chave. Ele funciona como try em outras linguagens: se o resultado for Err, a função retorna imediatamente esse erro. Se for Ok, extrai o valor.
1.2. Tratamento com match
A forma mais explícita é usar match:
let result = read_file("config.txt");
match result { Ok(content) => println!("Arquivo lido: {}", content), Err(e) => eprintln!("Erro: {}", e),}Isso é análogo ao try/catch, porém mais flexível e controlado.
1.3. Métodos auxiliares
.unwrap()— causa pânico em caso de erro (apenas para protótipos)..expect(msg)— causa pânico com mensagem personalizada..unwrap_or(default)— retorna um valor padrão..is_ok()/.is_err()— verificação de estado.
2. Option<T>: trabalho seguro com ausência de valor
Option é outro enum com as variantes Some(T) e None. Ele substitui null e nullptr, eliminando erros de desreferenciação de ponteiro nulo.
2.1. Exemplo de uso
fn find_user(id: u32) -> Option<String> { if id == 1 { Some("Alice".to_string()) } else { None }}
let user = find_user(2);println!("Usuário: {}", user.unwrap_or("Convidado".to_string()));2.2. Combinação com Result
Frequentemente, Option é convertido para Result para tratamento uniforme:
fn get_user_name(id: u32) -> Result<String, String> { find_user(id).ok_or("Usuário não encontrado".to_string())}O método .ok_or() transforma Some em Ok e None em Err.
3. Operador ?: substituição elegante para try/catch
O operador ? é um açúcar sintático que desempacota automaticamente Result ou Option. Ele só está disponível em funções que retornam o tipo correspondente.
3.1. Exemplo com encadeamento de chamadas
use std::num::ParseIntError;
fn parse_and_add(a: &str, b: &str) -> Result<i32, ParseIntError> { let x: i32 = a.parse()?; let y: i32 = b.parse()?; Ok(x
```