Rust

Como Usar Pattern Matching com match e if let em Rust em Produção

9 min de leitura

Como Usar Pattern Matching com match e if let em Rust em Produção

Introdução ao Pattern Matching em Rust Pattern matching é um dos recursos mais poderosos de Rust. Ele permite que você desconstrua valores complexos de forma segura e expressiva, comparando-os contra padrões específicos. Diferente de simples comparações, pattern matching em Rust verifica exaustividade — o compilador garante que você tratou todos os casos possíveis, eliminando erros lógicos comuns. Rust oferece duas construções principais: para análise completa e para casos simples. Ambas funcionam com enums, tuplas, structs e até literais. A verdadeira magia está em como o compilador força você a ser exhaustivo e seguro, rejeitando código incompleto em tempo de compilação. A Expressão : Poder Total O é uma expressão que avalia um padrão contra múltiplos braços. Ao contrário de switches em outras linguagens, em Rust é exhaustivo — você deve cobrir todos os casos possíveis ou usar o padrão coringa rust enum Resultado { Sucesso(String), Erro(i32), Pendente, } fn processar(res: Resultado) -> String { match res { Resultado::Sucesso(mensagem) => {

<h2>Introdução ao Pattern Matching em Rust</h2>

<p>Pattern matching é um dos recursos mais poderosos de Rust. Ele permite que você desconstrua valores complexos de forma segura e expressiva, comparando-os contra padrões específicos. Diferente de simples comparações, pattern matching em Rust verifica <strong>exaustividade</strong> — o compilador garante que você tratou todos os casos possíveis, eliminando erros lógicos comuns.</p>

<p>Rust oferece duas construções principais: <code>match</code> para análise completa e <code>if let</code> para casos simples. Ambas funcionam com enums, tuplas, structs e até literais. A verdadeira magia está em como o compilador força você a ser exhaustivo e seguro, rejeitando código incompleto em tempo de compilação.</p>

<h2>A Expressão <code>match</code>: Poder Total</h2>

<p>O <code>match</code> é uma expressão que avalia um padrão contra múltiplos braços. Ao contrário de switches em outras linguagens, <code>match</code> em Rust é exhaustivo — você <strong>deve</strong> cobrir todos os casos possíveis ou usar o padrão coringa <code>_</code>.</p>

<h3>Exemplo Básico com Enum</h3>

<pre><code class="language-rust">enum Resultado {

Sucesso(String),

Erro(i32),

Pendente,

}

fn processar(res: Resultado) -&gt; String {

match res {

Resultado::Sucesso(mensagem) =&gt; {

format!(&quot;✓ {}&quot;, mensagem)

}

Resultado::Erro(codigo) =&gt; {

format!(&quot;✗ Erro {}&quot;, codigo)

}

Resultado::Pendente =&gt; String::from(&quot;⏳ Aguardando...&quot;),

}

}

fn main() {

let resultado = Resultado::Sucesso(&quot;Dados carregados&quot;.to_string());

println!(&quot;{}&quot;, processar(resultado)); // ✓ Dados carregados

}</code></pre>

<p>Neste exemplo, cada variante do enum é tratada explicitamente. Se você esquecer um caso, o compilador recusa compilar o código. A desconstrução <code>Sucesso(mensagem)</code> extrai o valor interno automaticamente.</p>

<h3>Padrões Avançados</h3>

<p>O <code>match</code> suporta padrões complexos com guards (condições extras) e desestruturação profunda:</p>

<pre><code class="language-rust">fn analisar_ponto(ponto: (i32, i32)) -&gt; &amp;&#039;static str {

match ponto {

(0, 0) =&gt; &quot;Origem&quot;,

(0, y) if y &gt; 0 =&gt; &quot;Eixo Y positivo&quot;,

(0, _) =&gt; &quot;Eixo Y negativo&quot;,

(x, 0) if x &gt; 0 =&gt; &quot;Eixo X positivo&quot;,

(x, 0) =&gt; &quot;Eixo X negativo&quot;,

(x, y) if x == y =&gt; &quot;Diagonal principal&quot;,

(x, y) if x == -y =&gt; &quot;Diagonal secundária&quot;,

_ =&gt; &quot;Ponto genérico&quot;,

}

}

fn main() {

println!(&quot;{}&quot;, analisar_ponto((0, 5))); // Eixo Y positivo

println!(&quot;{}&quot;, analisar_ponto((3, 3))); // Diagonal principal

println!(&quot;{}&quot;, analisar_ponto((4, 2))); // Ponto genérico

}</code></pre>

<p>Os guards com <code>if</code> permitem condições extras além do padrão estrutural. O padrão <code>_</code> captura qualquer valor sem usá-lo — perfeito para casos que você não precisa processar.</p>

<h2><code>if let</code>: Simplicidade para Casos Únicos</h2>

<p>Quando você só se importa com <strong>um</strong> padrão específico, <code>if let</code> oferece sintaxe mais clara e concisa. Ele é açúcar sintático sobre <code>match</code> com dois braços: um padrão que interessa e <code>_</code> implícito para o resto.</p>

<h3>Quando Usar <code>if let</code></h3>

<pre><code class="language-rust">enum Cargo {

Presidente(String),

Desenvolvedor(String),

Indefinido,

}

fn verificar_desenvolvedor(cargo: Cargo) {

if let Cargo::Desenvolvedor(nome) = cargo {

println!(&quot;Desenvolvedor: {}&quot;, nome);

}

}

fn main() {

let meu_cargo = Cargo::Desenvolvedor(&quot;Alice&quot;.to_string());

verificar_desenvolvedor(meu_cargo); // Desenvolvedor: Alice

}</code></pre>

<p>A vantagem aqui é legibilidade. Se você só precisa agir quando o cargo é desenvolvedor, <code>if let</code> é mais direto que <code>match</code> com vários braços vazios.</p>

<h3>Combinando com <code>else if let</code></h3>

<p>Para múltiplas condições correlatas, encadeie <code>if let</code>:</p>

<pre><code class="language-rust">fn classificar_valor(valor: Option&lt;i32&gt;) {

if let Some(n) = valor {

if n &gt; 100 {

println!(&quot;Grande: {}&quot;, n);

} else {

println!(&quot;Pequeno: {}&quot;, n);

}

} else {

println!(&quot;Sem valor&quot;);

}

}

fn main() {

classificar_valor(Some(150)); // Grande: 150

classificar_valor(None); // Sem valor

}</code></pre>

<p>Alternativamente, use <code>else if let</code> para sintaxe mais plana:</p>

<pre><code class="language-rust">fn classificar_resultado(res: Result&lt;i32, String&gt;) {

if let Ok(num) = res {

println!(&quot;Sucesso: {}&quot;, num);

} else if let Err(erro) = res {

println!(&quot;Erro: {}&quot;, erro);

}

}</code></pre>

<h2>Padrões Avançados e Desestruturação</h2>

<p>Rust permite desestruturação profunda em structs e enums aninhados, tornando code muito expressivo sem variáveis intermediárias.</p>

<h3>Desestruturação de Structs</h3>

<pre><code class="language-rust">struct Usuario {

nome: String,

idade: u32,

email: Option&lt;String&gt;,

}

fn processar_usuario(usuario: Usuario) {

match usuario {

Usuario { nome, idade: 18..=65, email: Some(e) } =&gt; {

println!(&quot;{} ({}) - {}&quot;, nome, idade, e);

}

Usuario { nome, idade: 18..=65, email: None } =&gt; {

println!(&quot;{} ({}) - sem email&quot;, nome, idade);

}

Usuario { nome, idade, .. } =&gt; {

println!(&quot;{} - fora da faixa etária ({})&quot;, nome, idade);

}

}

}

fn main() {

let user = Usuario {

nome: &quot;Bob&quot;.to_string(),

idade: 30,

email: Some(&quot;bob@example.com&quot;.to_string()),

};

processar_usuario(user);

}</code></pre>

<p>Os ranges <code>18..=65</code> e o coringa <code>..</code> para ignorar campos desnecessários tornam o padrão altamente legível. O compilador valida que todos os campos necessários são cobertos.</p>

<h3>Ranges e Literais</h3>

<pre><code class="language-rust">fn descrever_dia(dia: u32) -&gt; &amp;&#039;static str {

match dia {

1 | 3 | 5 | 7 | 9 | 11 =&gt; &quot;Mês com 31 dias&quot;, 4 | 6 | 9 | 11 =&gt; &quot;Mês com 30 dias&quot;,

2 =&gt; &quot;Fevereiro&quot;,

_ =&gt; &quot;Dia inválido&quot;,

}

}

fn categoria_idade(idade: u32) -&gt; &amp;&#039;static str {

match idade {

0..=12 =&gt; &quot;Criança&quot;,

13..=19 =&gt; &quot;Adolescente&quot;,

20..=59 =&gt; &quot;Adulto&quot;,

60.. =&gt; &quot;Idoso&quot;,

_ =&gt; unreachable!(),

}

}

fn main() {

println!(&quot;{}&quot;, categoria_idade(25)); // Adulto

}</code></pre>

<p>Padrões de múltiplas alternativas com <code>|</code> e ranges com <code>..</code> cobrem casos comuns sem verbosidade. O <code>unreachable!()</code> marca código que nunca deve ser alcançado, ajudando o compilador com análise de fluxo.</p>

<h2>Conclusão</h2>

<p><strong>Pattern matching é fundamental em Rust</strong> porque garante segurança em tempo de compilação. O compilador força você a considerar todos os cenários, eliminando bugs antes que ocorram. Use <code>match</code> para análise exaustiva e múltiplos padrões; use <code>if let</code> para casos únicos onde você só se importa com um resultado específico.</p>

<p><strong>Desestruturação reduz código redundante</strong> — em vez de acessar campos manualmente, deixe o padrão extrair valores diretamente nas variáveis que você precisa. Guards e ranges tornam lógica complexa expressa elegantemente.</p>

<p><strong>O compilador é seu aliado</strong> — rejeições iniciais parecem frustrantes, mas protegem você de horas de debugging. Abraçe a natureza exhaustiva do pattern matching e escreva código que é ao mesmo tempo seguro e legível.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://doc.rust-lang.org/book/ch18-00-patterns.html" target="_blank" rel="noopener noreferrer">The Rust Book - Pattern Matching</a></li>

<li><a href="https://doc.rust-lang.org/reference/patterns.html" target="_blank" rel="noopener noreferrer">Rust Reference - Patterns</a></li>

<li><a href="https://doc.rust-lang.org/rust-by-example/flow_control/match.html" target="_blank" rel="noopener noreferrer">Rust by Example - match</a></li>

<li><a href="https://blog.rust-lang.org/" target="_blank" rel="noopener noreferrer">Official Rust Blog - Pattern Matching</a></li>

<li><a href="https://google.github.io/comprehensive-rust/" target="_blank" rel="noopener noreferrer">Comprehensive Rust - Pattern Matching</a></li>

</ul>

Comentários

Mais em Rust

Dominando Banco de Dados em Rust com SQLx e PostgreSQL em Projetos Reais
Dominando Banco de Dados em Rust com SQLx e PostgreSQL em Projetos Reais

Introdução ao SQLx e PostgreSQL em Rust SQLx é um driver SQL assincrônico e t...

Como Usar Enums em Rust: Definição, Variantes e Dados Associados em Produção
Como Usar Enums em Rust: Definição, Variantes e Dados Associados em Produção

Introdução: O Que São Enums em Rust? Enums (enumerações) são um dos pilares d...

Channels em Rust: Comunicação entre Threads com mpsc na Prática
Channels em Rust: Comunicação entre Threads com mpsc na Prática

Entendendo Channels e o Padrão MPSC Channels (canais) em Rust são primitivas...