<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) -> String {
match res {
Resultado::Sucesso(mensagem) => {
format!("✓ {}", mensagem)
}
Resultado::Erro(codigo) => {
format!("✗ Erro {}", codigo)
}
Resultado::Pendente => String::from("⏳ Aguardando..."),
}
}
fn main() {
let resultado = Resultado::Sucesso("Dados carregados".to_string());
println!("{}", 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)) -> &'static str {
match ponto {
(0, 0) => "Origem",
(0, y) if y > 0 => "Eixo Y positivo",
(0, _) => "Eixo Y negativo",
(x, 0) if x > 0 => "Eixo X positivo",
(x, 0) => "Eixo X negativo",
(x, y) if x == y => "Diagonal principal",
(x, y) if x == -y => "Diagonal secundária",
_ => "Ponto genérico",
}
}
fn main() {
println!("{}", analisar_ponto((0, 5))); // Eixo Y positivo
println!("{}", analisar_ponto((3, 3))); // Diagonal principal
println!("{}", 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!("Desenvolvedor: {}", nome);
}
}
fn main() {
let meu_cargo = Cargo::Desenvolvedor("Alice".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<i32>) {
if let Some(n) = valor {
if n > 100 {
println!("Grande: {}", n);
} else {
println!("Pequeno: {}", n);
}
} else {
println!("Sem valor");
}
}
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<i32, String>) {
if let Ok(num) = res {
println!("Sucesso: {}", num);
} else if let Err(erro) = res {
println!("Erro: {}", 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<String>,
}
fn processar_usuario(usuario: Usuario) {
match usuario {
Usuario { nome, idade: 18..=65, email: Some(e) } => {
println!("{} ({}) - {}", nome, idade, e);
}
Usuario { nome, idade: 18..=65, email: None } => {
println!("{} ({}) - sem email", nome, idade);
}
Usuario { nome, idade, .. } => {
println!("{} - fora da faixa etária ({})", nome, idade);
}
}
}
fn main() {
let user = Usuario {
nome: "Bob".to_string(),
idade: 30,
email: Some("bob@example.com".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) -> &'static str {
match dia {
1 | 3 | 5 | 7 | 9 | 11 => "Mês com 31 dias", 4 | 6 | 9 | 11 => "Mês com 30 dias",
2 => "Fevereiro",
_ => "Dia inválido",
}
}
fn categoria_idade(idade: u32) -> &'static str {
match idade {
0..=12 => "Criança",
13..=19 => "Adolescente",
20..=59 => "Adulto",
60.. => "Idoso",
_ => unreachable!(),
}
}
fn main() {
println!("{}", 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>