Introdução: por que Rust não usa try/catch?
Na maioria das linguagens de programação (JavaScript, Python, Java), o tratamento de erros é baseado no mecanismo de exceções e na construção try/catch. No entanto, em Rust, essa abordagem está ausente. Em vez disso, os desenvolvedores usam um poderoso sistema de tipos, que torna os erros parte da assinatura da função. Isso não é um acaso, mas uma decisão arquitetônica bem pensada: Rust busca previsibilidade e segurança em tempo de compilação, excluindo falhas ocultas do programa.
Neste artigo, vamos abordar:
- Por que Rust abandonou o
try/catch; - Como funcionam as principais ferramentas:
Option,Result,unwrap(),expect(); - O que é o operador
?(ponto de interrogação); - Quando usar
panic!e a macrotry!(obsoleta); - Práticas modernas de tratamento de erros em Rust 2024.
1. Fundamentos: Option e Result — os dois principais tipos
Tipo Option: quando um valor pode estar ausente
Option<T> é uma enumeração (enum) que representa ou um valor (Some(T)) ou sua ausência (None). Ele é ideal para situações em que um erro no sentido clássico não ocorre, mas o resultado pode ser vazio.
fn encontrar_usuario(id: u32) -> Option<String> {
let banco = vec!["Alice".to_string(), "Bob".to_string()];
if id < banco.len() {
Some(banco[id].clone())
} else {
None
}
}
fn main() {
match encontrar_usuario(1) {
Some(nome) => println!("Encontrado: {}", nome),
None => println!("Usuário não encontrado"),
}
}
Tipo Result: sucesso ou erro
Result<T, E> é a ferramenta principal para tratamento de erros. Ele também é um enum: Ok(T) (sucesso) ou Err(E) (erro).
use std::fs::File;
use std::io::Read;
fn ler_arquivo(caminho: &str) -> Result<String, std::io::Error> {
let mut arquivo = File::open(caminho)?;
let mut conteudo = String::new();
arquivo.read_to_string(&mut conteudo)?;
Ok(conteudo)
}
2. Operador ? (ponto de interrogação) — substituto do try/catch
O operador ? é a principal forma de propagar erros para cima em Rust. Ele substitui construções match verbosas e torna o código limpo. Se o resultado for Ok, o valor é extraído e atribuído à variável. Se for Err, a função retorna imediatamente esse erro.
use std::fs;
use std::io;
fn copiar_arquivo(origem: &str, destino: &str) -> io::Result<()> {
let dados = fs::read(origem)?; // se erro — saída
fs::write(destino, &dados)?; // se erro — saída
Ok(())
}
fn main() -> io::Result<()> {
copiar_arquivo("source.txt", "dest.txt")?;
println!("Arquivo copiado com sucesso");
Ok(())
}
Importante: o operador ? funciona apenas em funções que retornam Result, Option ou tipos que implementam a trait FromResidual.
3. panic!, unwrap e expect — quando o erro é fatal
Macro panic!
panic!() causa o encerramento abrupto do programa (stack unwinding). É usado apenas em situações onde a continuação da execução é impossível ou sem sentido: erros na lógica do programa, violação de invariantes, testes.
fn divisao(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("Divisão por zero!");
}
a / b
}
Métodos unwrap() e expect()
unwrap() é uma forma rápida de obter o valor de um Option ou Result. Se houver None ou Err internamente — pro
```