<h2>O Operador ? em Rust: Propagação de Erros Elegante</h2>
<h3>O que é o Operador ?</h3>
<p>O operador <code>?</code> é um atalho poderoso em Rust para propagação de erros. Quando usado em uma função que retorna <code>Result<T, E></code> ou <code>Option<T></code>, ele funciona como um "desempacotador inteligente": se o valor é um sucesso (<code>Ok</code> ou <code>Some</code>), ele extrai o conteúdo; se for um erro (<code>Err</code> ou <code>None</code>), ele imediatamente retorna esse erro da função atual. Isso elimina a necessidade de escrever condicionais verbosos com <code>match</code> ou <code>unwrap</code>, tornando o código mais legível e seguro.</p>
<p>O <code>?</code> foi introduzido no Rust 1.13 como resposta à necessidade de tratamento de erros mais ergonômico. Em vez de usar <code>try!</code> (macro anterior), você escreve código que flui naturalmente enquanto mantém a segurança de tipos que Rust oferece.</p>
<pre><code class="language-rust">use std::fs;
use std::io;
// Sem o operador ?
fn ler_arquivo_verboso(caminho: &str) -> Result<String, io::Error> {
let conteudo = match fs::read_to_string(caminho) {
Ok(dados) => dados,
Err(e) => return Err(e),
};
Ok(conteudo)
}
// Com o operador ?
fn ler_arquivo(caminho: &str) -> Result<String, io::Error> {
let conteudo = fs::read_to_string(caminho)?;
Ok(conteudo)
}</code></pre>
<h3>Sintaxe e Casos de Uso</h3>
<p>O operador <code>?</code> pode ser usado em qualquer função que retorna <code>Result<T, E></code> ou <code>Option<T></code>. A sintaxe é simples: coloque <code>?</code> imediatamente após a expressão que você quer desempacotar. Você não pode usar <code>?</code> em funções que retornam <code>()</code> (void) — a função precisa retornar um tipo que implemente o trait <code>Try</code>.</p>
<p>Um detalhe importante: quando você usa <code>?</code> com <code>Result</code>, o erro é automaticamente convertido usando o trait <code>From</code>. Isso significa que se sua função retorna <code>Result<T, MinhaError></code>, mas uma operação retorna <code>Result<T, OutraError></code>, Rust tentará converter <code>OutraError</code> em <code>MinhaError</code> usando <code>From::from()</code>. Isso é extraordinariamente conveniente para consolidar múltiplos tipos de erro.</p>
<pre><code class="language-rust">use std::fs;
use std::num::ParseIntError;
#[derive(Debug)]
enum MinhaError {
IoError(std::io::Error),
ParseError(ParseIntError),
}
impl From<std::io::Error> for MinhaError {
fn from(err: std::io::Error) -> Self {
MinhaError::IoError(err)
}
}
impl From<ParseIntError> for MinhaError {
fn from(err: ParseIntError) -> Self {
MinhaError::ParseError(err)
}
}
fn processar_arquivo(caminho: &str) -> Result<i32, MinhaError> {
let conteudo = fs::read_to_string(caminho)?; // Pode retornar IoError
let numero = conteudo.trim().parse::<i32>()?; // Pode retornar ParseIntError
Ok(numero)
}</code></pre>
<h3>Operador ? com Option</h3>
<p>Além de <code>Result</code>, o operador <code>?</code> também funciona com <code>Option<T></code>. Quando você usa <code>?</code> em um <code>Option</code>, se for <code>None</code>, a função retorna <code>None</code> imediatamente. Isso é útil para cadeias de operações opcionais onde qualquer <code>None</code> significa que não há resultado válido.</p>
<p>A combinação de <code>?</code> com métodos que retornam <code>Option</code> torna código que busca valores aninhados extremamente limpo. Por exemplo, navegar por estruturas de dados aninhadas que podem não existir fica muito mais expressivo do que usar múltiplos <code>if let</code> ou <code>match</code>.</p>
<pre><code class="language-rust">fn extrair_idade(dados: Option<(&str, u32)>) -> Option<u32> {
let (_nome, idade) = dados?;
let idade_dobrada = idade.checked_mul(2)?; // Retorna None se overflow
Some(idade_dobrada)
}
fn main() {
println!("{:?}", extrair_idade(Some(("Alice", 25)))); // Some(50)
println!("{:?}", extrair_idade(None)); // None
}</code></pre>
<h3>Boas Práticas e Limitações</h3>
<p>O operador <code>?</code> não é uma bala de prata. Há situações onde <code>match</code> ou <code>if let</code> são mais apropriados: quando você precisa fazer algo específico com o erro (logging, recuperação parcial) antes de propagar, ou quando precisa decidir entre múltiplos caminhos baseado no tipo exato do erro. Use <code>?</code> para o caminho "feliz" direto; use <code>match</code> para lógica condicional complexa.</p>
<p>Uma limitação prática: você não pode usar <code>?</code> em closures que retornam tipos simples. Se um closure retorna <code>()</code>, você não pode usar <code>?</code> dentro dele. Para isso, use <code>.map_err()</code> ou converta a closure em uma função separada. Além disso, em <code>main()</code>, você não pode usar <code>?</code> diretamente — a função <code>main</code> retorna <code>()</code>. Porém, em Rust moderno, você pode fazer <code>fn main() -> Result<(), Box<dyn std::error::Error>></code> para permitir <code>?</code>.</p>
<pre><code class="language-rust">fn main() -> Result<(), Box<dyn std::error::Error>> {
let dados = std::fs::read_to_string("config.txt")?;
let numero: i32 = dados.trim().parse()?;
println!("Valor: {}", numero);
Ok(())
}</code></pre>
<h2>Conclusão</h2>
<p>O operador <code>?</code> em Rust é um exemplo brilhante de design de linguagem: fornece conveniência sem sacrificar segurança. Ele elimina boilerplate verboso enquanto força o programador a estar ciente de que erros podem ocorrer. Aprenda a usá-lo naturalmente para seu código fluir entre operações que podem falhar, combine-o com traits <code>From</code> customizados para consolidar tipos de erro, e reserve <code>match</code> para lógica que exige decisões explícitas sobre cada tipo de falha.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://doc.rust-lang.org/book/ch09-00-error-handling.html" target="_blank" rel="noopener noreferrer">The Rust Book - Error Handling</a></li>
<li><a href="https://doc.rust-lang.org/rust-by-example/error/result.html" target="_blank" rel="noopener noreferrer">Rust by Example - Result</a></li>
<li><a href="https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator" target="_blank" rel="noopener noreferrer">Official Rust Documentation - The ? operator</a></li>
<li><a href="https://www.infoq.com/articles/rust-error-handling/" target="_blank" rel="noopener noreferrer">Error Handling in Rust - Medium Article</a></li>
<li><a href="https://www.rust-lang.org/learn" target="_blank" rel="noopener noreferrer">Rust Programming Language Official Guide</a></li>
</ul>