intermediário⏱45 minutosLição 1 de 10
Match e Padrões
Domine a correspondência de padrões do Rust: literais, intervalos, desestruturação, guards e ergonomia avançada de match
Match e Padrões
A correspondência de padrões é um dos recursos mais poderosos do Rust. Ela combina lógica condicional com desestruturação em uma única sintaxe expressiva.
Revisão da Sintaxe de Match
rust
fn descrever_numero(n: i32) -> &'static str {
match n {
0 => "zero",
1 | 2 | 3 => "pequeno",
4..=10 => "médio",
_ if n > 100 => "grande", // Guard
_ => "outro",
}
}Tipos de Padrões
Padrões Literais
Combine valores específicos:
rust
fn eh_fim_de_semana(dia: &str) -> bool {
match dia {
"sábado" | "domingo" => true,
_ => false,
}
}Padrões de Intervalo
rust
fn nota_por_pontuacao(pontos: u8) -> &'static str {
match pontos {
90..=100 => "A",
80..=89 => "B",
70..=79 => "C",
60..=69 => "D",
0..=59 => "F",
}
}
fn corresponder_char(c: char) -> &'static str {
match c {
'a'..='z' => "minúsculo",
'A'..='Z' => "maiúsculo",
'0'..='9' => "dígito",
_ => "outro",
}
}Note
Padrões de Desestruturação
Structs
rust
struct Ponto {
x: i32,
y: i32,
}
fn main() {
let p = Ponto { x: 10, y: 20 };
match p {
Ponto { x, y } => println!("({x}, {y})"),
}
// Desestruturação parcial com ..
match p {
Ponto { x, .. } => println!("x: {x}"),
}
// Renomear campos
match p {
Ponto { x: a, y: b } => println!("({a}, {b})"),
}
}Enums
rust
enum Mensagem {
Sair,
Mover { x: i32, y: i32 },
Escrever(String),
MudarCor(i32, i32, i32),
}
fn manipular(msg: Mensagem) {
match msg {
Mensagem::Sair => println!("sair"),
Mensagem::Mover { x, y } => println!("mover para ({x}, {y})"),
Mensagem::Escrever(texto) => println!("{texto}"),
Mensagem::MudarCor(r, g, b) => println!("RGB({r}, {g}, {b})"),
}
}Tuplas
rust
fn descrever_ponto(p: (i32, i32, i32)) -> &'static str {
match p {
(0, 0, 0) => "origem",
(0, 0, _) => "no eixo z",
(0, _, _) => "no plano x=0",
(_, 0, _) => "no plano y=0",
(_, _, 0) => "no plano z=0",
_ => "no espaço",
}
}Arrays e Slices
rust
fn primeiro_e_ultimo(arr: &[i32]) -> Option<(&i32, &i32)> {
match arr {
[primeiro, .., ultimo] => Some((primeiro, ultimo)),
_ => None,
}
}
fn soma_dois_primeiros(arr: &[i32]) -> i32 {
match arr {
[a, b, ..] => a + b,
[a] => *a,
[] => 0,
}
}Success
Padrões de slice com .. (padrão de resto) facilitam a correspondência na estrutura de coleções. O compilador verifica exaustividade mesmo com padrões complexos.
Padrões de Guarda
Adicione condições extras com if:
rust
fn classificar_numero(n: i32) -> &'static str {
match n {
x if x < 0 => "negativo",
x if x % 2 == 0 => "positivo par",
_ => "positivo ímpar",
}
}
fn corresponder_par(par: (i32, i32)) -> &'static str {
match par {
(x, y) if x == y => "iguais",
(x, y) if x + y == 0 => "negativos",
(x, _) if x > 0 => "primeiro positivo",
_ => "outro",
}
}Ligações @
Vincule o valor correspondido a um nome enquanto desestrutura:
rust
fn inspecionar(valor: Option<i32>) {
match valor {
Some(x @ 0..=10) => println!("positivo pequeno: {x}"),
Some(x @ 11..=100) => println!("positivo médio: {x}"),
Some(x) => println!("positivo grande: {x}"),
None => println!("nenhum"),
}
}
// Com desestruturação aninhada
struct Pessoa {
nome: String,
idade: u8,
}
fn cumprimentar(pessoa: Pessoa) {
match pessoa {
Pessoa { nome, idade: idade @ 0..=12 } => {
println!("Oi {nome}, você tem {idade} — é criança!");
}
Pessoa { nome, idade: idade @ 13..=19 } => {
println!("Ei {nome}, você tem {idade} — é adolescente!");
}
Pessoa { nome, idade } => {
println!("Olá {nome}, você tem {idade}.");
}
}
}Múltiplos Padrões com |
rust
fn eh_vogal(c: char) -> bool {
matches!(c, 'a' | 'e' | 'i' | 'o' | 'u' | 'A' | 'E' | 'I' | 'O' | 'U')
}
fn descrever_dia(n: u8) -> &'static str {
match n {
1 | 7 => "fim de semana",
2..=6 => "dia útil",
_ => "inválido",
}
}A Macro matches!
Para verificações booleanas simples de padrões:
rust
fn main() {
let foo = Some(42);
assert!(matches!(foo, Some(x) if x > 0));
let arr = [1, 2, 3];
assert!(matches!(arr, [1, _, _]));
}Ergonomia de Match
Rust desreferencia automaticamente em padrões de match:
rust
fn main() {
let x = &Some(42);
// Sem ergonomia (pré-2018):
match x {
&Some(ref y) => println!("{y}"),
&None => (),
}
// Com ergonomia (2018+):
match x {
Some(y) => println!("{y}"),
None => (),
}
}Padrões Ref
Use ref e ref mut para tomar valores emprestados:
rust
fn main() {
let s = Some(String::from("olá"));
match s {
Some(ref s) => println!("emprestado: {s}"), // s é &String
None => (),
}
println!("ainda possuído: {:?}", s); // s não foi movido
let mut t = Some(String::from("mundo"));
match t {
Some(ref mut s) => *s = String::from("alterado"),
None => (),
}
println!("{:?}", t); // Some("alterado")
}Correspondência de Padrões em Outros Contextos
Declarações let
rust
let (x, y, z) = (1, 2, 3);
let Ponto { x: a, y: b } = Ponto { x: 10, y: 20 };Parâmetros de Função
rust
fn imprimir_coordenadas(&(x, y): &(i32, i32)) {
println!("({x}, {y})");
}
fn somar_coordenadas(Ponto { x, y }: &Ponto) -> i32 {
x + y
}if let / while let
rust
fn main() {
let mut pilha = vec![1, 2, 3];
while let Some(topo) = pilha.pop() {
println!("{topo}");
}
let coords = (0, 5);
if let (0, y) = coords {
println!("no eixo y em {y}");
}
}for loops
rust
fn main() {
let pares = [(1, 2), (3, 4), (5, 6)];
for (a, b) in &pares {
println!("{a} + {b} = {}", a + b);
}
let mapa = std::collections::HashMap::from([("a", 1), ("b", 2)]);
for (chave, valor) in &mapa {
println!("{chave}: {valor}");
}
}Correspondência de Tipos de Referência e Ponteiro
rust
fn main() {
let x = 42;
let y = &x;
let z = Box::new(x);
match y {
42 => println!("corresponde ao valor (auto-deref)"),
_ => println!("sem correspondência"),
}
match &z {
42 => println!("Box também corresponde"),
_ => println!("sem correspondência"),
}
}Mundo Real: Avaliador de Expressões
rust
#[derive(Debug)]
enum Expr {
Lit(i32),
Add(Box<Expr>, Box<Expr>),
Sub(Box<Expr>, Box<Expr>),
Mul(Box<Expr>, Box<Expr>),
Div(Box<Expr>, Box<Expr>),
}
fn avaliar(expr: &Expr) -> Option<i32> {
match expr {
Expr::Lit(n) => Some(*n),
Expr::Add(l, r) => Some(avaliar(l)? + avaliar(r)?),
Expr::Sub(l, r) => Some(avaliar(l)? - avaliar(r)?),
Expr::Mul(l, r) => Some(avaliar(l)? * avaliar(r)?),
Expr::Div(l, r) => {
let r_val = avaliar(r)?;
if r_val == 0 {
None
} else {
Some(avaliar(l)? / r_val)
}
}
}
}
fn main() {
let expr = Expr::Add(
Box::new(Expr::Lit(10)),
Box::new(Expr::Mul(
Box::new(Expr::Lit(3)),
Box::new(Expr::Lit(5)),
)),
);
println!("{:?}", avaliar(&expr)); // Some(25)
}Perguntas de Prática
- O que o padrão
..significa ao desestruturar um struct? - Como corresponder um intervalo de valores em match?
- Qual a diferença entre
|e..=em padrões? - Quando usar um guard de match em vez de um match aninhado?
- O que a ligação
@faz? - Como a correspondência de padrões em
if letdifere dematch? - O que são padrões ref e quando são necessários?
- Como a macro
matches!simplifica a correspondência de padrões? - Você pode desestruturar um slice com padrões? Dê um exemplo.
- Como Rust lida com a desreferenciação automática em padrões de match?
Progresso10%