Panic em unwrap(): Como Evitar
O panic por unwrap() é provavelmente o erro de runtime mais comum no Rust. Ele ocorre quando você chama .unwrap() em um Option::None ou Result::Err. Diferente dos erros de compilação, esse problema só aparece durante a execução do programa.
A Mensagem de Erro
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:5:30
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Para Result:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value:
Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:3:47
O Que Significa
Option<T> e Result<T, E> são os tipos do Rust para representar valores que podem estar ausentes ou operações que podem falhar:
Option<T>pode serSome(valor)ouNoneResult<T, E>pode serOk(valor)ouErr(erro)
O método .unwrap() extrai o valor interno, mas entra em panic se não houver valor. É como dizer “tenho certeza absoluta que há um valor aqui” — e se não houver, o programa aborta.
Usar unwrap() em produção é geralmente um erro. É aceitável em protótipos, testes e situações onde você tem certeza lógica de que o valor existe, mas mesmo assim há alternativas melhores.
Código com Erro
fn main() {
let numeros = vec![1, 2, 3];
// PANIC: índice 10 não existe, get() retorna None
let valor = numeros.get(10).unwrap();
// PANIC: arquivo pode não existir
let conteudo = std::fs::read_to_string("inexistente.txt").unwrap();
// PANIC: parse pode falhar
let numero: i32 = "abc".parse().unwrap();
}
Como Resolver
Solução 1: Usar match para Tratamento Explícito
O tratamento mais completo e explícito:
fn main() {
let numeros = vec![1, 2, 3];
match numeros.get(10) {
Some(valor) => println!("Valor: {}", valor),
None => println!("Índice fora dos limites"),
}
match std::fs::read_to_string("config.txt") {
Ok(conteudo) => println!("Conteúdo: {}", conteudo),
Err(erro) => eprintln!("Erro ao ler arquivo: {}", erro),
}
}
Solução 2: Usar if let para Casos Simples
Quando você só se importa com o caso de sucesso:
fn main() {
let numeros = vec![1, 2, 3];
if let Some(valor) = numeros.get(0) {
println!("Primeiro: {}", valor);
}
if let Ok(conteudo) = std::fs::read_to_string("config.txt") {
println!("Conteúdo: {}", conteudo);
} else {
println!("Usando configuração padrão");
}
}
Solução 3: Usar unwrap_or e Variantes
Forneça um valor padrão em vez de entrar em panic:
fn main() {
let numeros = vec![1, 2, 3];
// Valor padrão fixo
let valor = numeros.get(10).unwrap_or(&0);
println!("Valor: {}", valor);
// Valor padrão calculado (lazy — só executa se necessário)
let valor = numeros.get(10).unwrap_or_else(|| {
println!("Usando padrão");
&0
});
// Para Result:
let conteudo = std::fs::read_to_string("config.txt")
.unwrap_or_else(|_| String::from("configuração padrão"));
// unwrap_or_default — usa Default::default()
let numero: i32 = "abc".parse().unwrap_or_default(); // 0
}
Solução 4: Usar o Operador ? (Propagação de Erros)
O operador ? é a forma idiomática do Rust para propagar erros:
use std::fs;
use std::io;
fn ler_configuracao() -> Result<String, io::Error> {
let conteudo = fs::read_to_string("config.txt")?; // Propaga o erro
Ok(conteudo.trim().to_string())
}
fn obter_porta() -> Result<u16, Box<dyn std::error::Error>> {
let config = ler_configuracao()?;
let porta: u16 = config.parse()?; // ? também funciona com outros erros
Ok(porta)
}
fn main() {
match obter_porta() {
Ok(porta) => println!("Porta: {}", porta),
Err(e) => eprintln!("Erro: {}", e),
}
}
O ? pode ser usado em funções que retornam Result ou Option. Ele “desempacota” o valor de sucesso ou retorna o erro imediatamente.
Solução 5: Usar map, and_then e Combinadores
Encadeie operações de forma funcional:
fn main() {
let input = "42";
// map transforma o valor interno
let resultado: Option<i32> = input.parse::<i32>().ok().map(|n| n * 2);
println!("{:?}", resultado); // Some(84)
// and_then encadeia operações que retornam Option/Result
let valor = Some("42")
.and_then(|s| s.parse::<i32>().ok())
.map(|n| n + 10)
.unwrap_or(0);
println!("{}", valor); // 52
// filter descarta valores que não atendem a condição
let positivo = Some(-5_i32).filter(|n| *n > 0);
println!("{:?}", positivo); // None
}
Solução 6: Usar expect() com Mensagem Clara
Se você precisa de unwrap (em testes ou situações seguras), use expect() com uma mensagem explicativa:
fn main() {
let porta: u16 = std::env::var("PORT")
.expect("Variável PORT deve estar definida")
.parse()
.expect("PORT deve ser um número válido");
}
expect() entra em panic como unwrap(), mas com uma mensagem customizada que facilita a depuração.
Quando unwrap() é Aceitável
| Situação | Aceitável? |
|---|---|
Testes (#[test]) | Sim |
| Exemplos e protótipos | Sim |
| Valor garantido pela lógica | Sim, mas prefira expect() |
| Código de produção | Nao — use ?, match ou combinadores |
| Input do usuário | Nunca |