Rust

Guia Completo de Result<T, E> em Rust: Tratamento de Erros sem Exceções

7 min de leitura

Guia Completo de Result<T, E> em Rust: Tratamento de Erros sem Exceções

O Tipo Result em Rust Result é o tipo fundamental de Rust para tratamento de erros, diferente completamente do modelo de exceções. Ele é um enum que representa dois estados: sucesso ( ) ou falha ( ). A principal vantagem é forçar o programador a lidar com possibilidades de erro em tempo de compilação, eliminando a surpresa de exceções não tratadas que vemos em linguagens como Java ou Python. Todo Result deve ser manipulado explicitamente. Ignorar um Result gera um aviso do compilador, garantindo que você nunca "deixe passar" um erro involuntariamente. Isso torna o código mais robusto e previsível desde o início do desenvolvimento. Padrões de Tratamento Básicos Match e Pattern Matching O é a forma mais explícita de lidar com Result. Você obriga o código a considerar ambos os casos: O é verbose mas extremamente claro. Cada branch deve retornar o mesmo tipo, o que incentiva tratamento coeso de erros. Operador (Propagação) O ponto de interrogação é açúcar

<h2>O Tipo Result&lt;T, E&gt; em Rust</h2>

<p>Result é o tipo fundamental de Rust para tratamento de erros, diferente completamente do modelo de exceções. Ele é um enum que representa dois estados: sucesso (<code>Ok(T)</code>) ou falha (<code>Err(E)</code>). A principal vantagem é forçar o programador a lidar com possibilidades de erro em tempo de compilação, eliminando a surpresa de exceções não tratadas que vemos em linguagens como Java ou Python.</p>

<pre><code class="language-rust">enum Result&lt;T, E&gt; {

Ok(T),

Err(E),

}</code></pre>

<p>Todo Result deve ser manipulado explicitamente. Ignorar um Result gera um aviso do compilador, garantindo que você nunca &quot;deixe passar&quot; um erro involuntariamente. Isso torna o código mais robusto e previsível desde o início do desenvolvimento.</p>

<h2>Padrões de Tratamento Básicos</h2>

<h3>Match e Pattern Matching</h3>

<p>O <code>match</code> é a forma mais explícita de lidar com Result. Você obriga o código a considerar ambos os casos:</p>

<pre><code class="language-rust">use std::fs::File;

fn ler_arquivo(caminho: &amp;str) -&gt; Result&lt;String, std::io::Error&gt; {

let mut arquivo = File::open(caminho)?;

let mut conteudo = String::new();

arquivo.read_to_string(&amp;mut conteudo)?;

Ok(conteudo)

}

fn main() {

match ler_arquivo(&quot;dados.txt&quot;) {

Ok(conteudo) =&gt; println!(&quot;Lido: {}&quot;, conteudo),

Err(erro) =&gt; eprintln!(&quot;Erro: {}&quot;, erro),

}

}</code></pre>

<p>O <code>match</code> é verbose mas extremamente claro. Cada branch deve retornar o mesmo tipo, o que incentiva tratamento coeso de erros.</p>

<h3>Operador <code>?</code> (Propagação)</h3>

<p>O ponto de interrogação é açúcar sintático que propaga erros automaticamente. Se uma operação falha, o erro é retornado imediatamente da função atual:</p>

<pre><code class="language-rust">fn processar_dados() -&gt; Result&lt;i32, Box&lt;dyn std::error::Error&gt;&gt; {

let arquivo = File::open(&quot;dados.txt&quot;)?;

let numero: i32 = std::fs::read_to_string(&quot;numero.txt&quot;)?

.trim()

.parse()?;

Ok(numero * 2)

}</code></pre>

<p>O <code>?</code> só funciona em funções que retornam Result. Para <code>main()</code>, use <code>.unwrap_or_else()</code> ou retorne <code>Result</code> do main (Rust 1.26+).</p>

<h2>Métodos Essenciais de Result</h2>

<p>Result fornece métodos combinadores que permitem transformar, filtrar e processar valores sem destruir a estrutura do tipo:</p>

<pre><code class="language-rust">fn main() {

let resultado: Result&lt;i32, String&gt; = Ok(5);

// map: transformar o valor Ok

let dobrado = resultado.map(|x| x * 2); // Ok(10)

// map_err: transformar o erro

let erro_msg = Err::&lt;i32, &amp;str&gt;(&quot;falha&quot;).map_err(|e| format!(&quot;Erro: {}&quot;, e));

// unwrap_or: valor padrão em caso de erro

let valor = Err::&lt;i32, String&gt;(&quot;problema&quot;.to_string()).unwrap_or(0); // 0

// and_then: acorrentar operações que retornam Result

let resultado = Ok(10)

.and_then(|x| if x &gt; 5 { Ok(x * 2) } else { Err(&quot;muito pequeno&quot;) });

// or_else: oferecer alternativa em caso de erro

let fallback = Err::&lt;i32, &amp;str&gt;(&quot;erro1&quot;)

.or_else(|_| Ok::&lt;i32, &amp;str&gt;(99)); // Ok(99)

}</code></pre>

<p>Esses combinadores eliminam a necessidade de múltiplos <code>match</code> aninhados, tornando o código funcional e elegante.</p>

<h2>Erros Customizados e Boas Práticas</h2>

<h3>Definindo Tipos de Erro Próprios</h3>

<p>Para aplicações reais, crie tipos de erro específicos que implementem <code>std::error::Error</code>:</p>

<pre><code class="language-rust">use std::error::Error;

use std::fmt;

#[derive(Debug)]

enum ErroArquivo {

NaoEncontrado(String),

PermissaoNegada,

FormatoInvalido(String),

}

impl fmt::Display for ErroArquivo {

fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {

match self {

ErroArquivo::NaoEncontrado(nome) =&gt;

write!(f, &quot;Arquivo não encontrado: {}&quot;, nome),

ErroArquivo::PermissaoNegada =&gt;

write!(f, &quot;Permissão negada ao acessar arquivo&quot;),

ErroArquivo::FormatoInvalido(motivo) =&gt;

write!(f, &quot;Formato inválido: {}&quot;, motivo),

}

}

}

impl Error for ErroArquivo {}

fn validar_json(conteudo: &amp;str) -&gt; Result&lt;(), ErroArquivo&gt; {

if conteudo.is_empty() {

return Err(ErroArquivo::FormatoInvalido(&quot;JSON vazio&quot;.into()));

}

Ok(())

}</code></pre>

<p>Tipos de erro customizados permitem que quem chama sua função entenda exatamente quais falhas são possíveis e trate cada uma apropriadamente.</p>

<h3>Crate <code>anyhow</code> para Contexto</h3>

<p>Em projetos maiores, use a crate <code>anyhow</code> para adicionar contexto aos erros sem criar tipos complexos:</p>

<pre><code class="language-rust">// Adicione ao Cargo.toml: anyhow = &quot;1.0&quot;

use anyhow::{Context, Result};

fn ler_config() -&gt; Result&lt;String&gt; {

let conteudo = std::fs::read_to_string(&quot;config.toml&quot;)

.context(&quot;Falha ao ler arquivo de configuração&quot;)?;

Ok(conteudo)

}

fn main() {

match ler_config() {

Ok(config) =&gt; println!(&quot;Config carregada&quot;),

Err(e) =&gt; eprintln!(&quot;Erro: {:?}&quot;, e),

}

}</code></pre>

<p>O <code>anyhow::Result</code> é equivalente a <code>Result&lt;T, Box&lt;dyn Error&gt;&gt;</code>, oferecendo flexibilidade com contexto.</p>

<h2>Conclusão</h2>

<p>Result&lt;T, E&gt; não é apenas um tipo — é uma filosofia de design que força você a ser explícito com erros. Os três aprendizados principais são: <strong>(1)</strong> todo erro deve ser tratado ou propagado conscientemente; <strong>(2)</strong> combinadores como <code>map</code>, <code>and_then</code> e <code>?</code> tornam o tratamento elegante e funcional; <strong>(3)</strong> erros customizados e crates como <code>anyhow</code> escalam bem em projetos reais.</p>

<p>Domine esses padrões e você escreverá código Rust robusto, previsível e mantível.</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/std/result/" target="_blank" rel="noopener noreferrer">Documentação std::result::Result</a></li>

<li><a href="https://docs.rs/anyhow/" target="_blank" rel="noopener noreferrer">Crate anyhow</a></li>

<li><a href="https://www.lpalmieri.com/posts/error-handling-rust/" target="_blank" rel="noopener noreferrer">Error Handling in Rust - A Comprehensive Guide</a></li>

</ul>

Comentários

Mais em Rust

Generics em Rust: Funções e Structs Parametrizadas por Tipo: Do Básico ao Avançado
Generics em Rust: Funções e Structs Parametrizadas por Tipo: Do Básico ao Avançado

Introdução aos Generics em Rust Generics são um dos pilares da programação mo...

Dominando Rc<T> e Arc<T> em Rust: Contagem de Referências em Projetos Reais
Dominando Rc<T> e Arc<T> em Rust: Contagem de Referências em Projetos Reais

Rc : Contagem de Referências em Single-Thread (Reference Counting) é um tipo...

Dominando Mutex<T> e RwLock<T> em Rust: Exclusão Mútua Segura em Projetos Reais
Dominando Mutex<T> e RwLock<T> em Rust: Exclusão Mútua Segura em Projetos Reais

Sincronização em Rust: Por que Mutex e RwLock Importam A segurança de memória...