11 — Pattern Matching
Meta do capítulo: despachar por forma e valor com
match. Referência:../language-reference/statements.md§5. Exemplo:../../examples/pattern_matching.aura.
match como statement
def classify(value) -> str {
match value {
case 0 { return "zero" }
case 1 { return "one" }
case n if n > 100 { return "big" }
case n if n > 0 { return "positive" }
case n if n < 0 { return "negative" }
case _ { return "other" }
}
return "unreachable"
}
0 -> zero
1 -> one
42 -> positive
200 -> big
-7 -> negative
Um corpo de case é um bloco { ... } ou uma seta -> expression. Não há fallthrough. As sintaxes de case : e => são rejeitadas com uma mensagem apontada.
Padrões
| Padrão | Exemplo | Vincula |
|---|---|---|
| Wildcard | case _ | nada |
| Literal | case 0, case "quit" | nada |
| Binding | case n | n |
| Guard | case n if n > 0 | n, apenas se o guard valer |
| Or-pattern | case 1 | 2 | — |
| Membro de enum | case Color.RED | — |
| Desestruturação de lista/tupla | case [a, b], case [first, *rest] | a, b, … |
Um case com guard não conta como catch-all: um match sobre um domínio escalar, bool ou enum sem case _ (ou binding nu) avisa E109.
Casando strings
def command(name) -> str {
match name {
case "quit" { return "exiting" }
case "help" { return "showing help" }
case _ { return "unknown command" }
}
return ""
}
Desestruturação
def sum_pair(pair) -> int {
match pair {
case [a, b] { return a + b }
case _ { return 0 }
}
return 0
}
*rest coleta o restante: case [first, *rest].
match como expressão
Com corpos em seta, match produz um valor:
def main() {
let n = 2
let text = match n {
case 1 -> "one"
case 2 -> "two"
case _ -> "other"
}
print(text) // two
}
A forma de expressão é hoisted para uma função auxiliar; a expressão final de cada case vira o valor retornado.
Enums
enum Color { RED, GREEN, BLUE }
def main() {
let c = Color.RED
match c {
case Color.RED { print("red") }
case _ { print("other color") }
}
}
Membros são separados por vírgula. Color.RED é um valor distinto, nunca a string "Red".
Pegadinhas
breakem um case com seta engole o próximocasecomo label. Como quebras de linha são insignificantes,case x -> breakseguido decase yparseia comobreak case. Use um corpo de bloco ou um;à direita.matchprecisa de um catch-all para um domínio finito, ouE109avisa (nunca falha um build). Umcase _ if condcom guard não suprime o warning.- Não há keyword
when; guards usamif.
Escolher match em vez de if
Use if/else if para um par de condições booleanas. Use match quando você está despachando por forma (desestruturação), por um enum, ou por vários valores literais ao mesmo tempo — ele mantém o valor inspecionado em um lugar só.
Padrões, precisamente
Um padrão não é uma expressão: ele nunca computa, apenas testa e vincula. Um nome nu em minúsculas sempre vincula (não compara contra uma variável de mesmo nome); membros pontuados capitalizados como Color.RED comparam contra um membro de enum. Essa distinção é o motivo de case n ser um binding catch-all enquanto case Color.RED é um teste de valor. Para comparar contra um valor computado, use um guard: case x if x == threshold.
Um or-pattern case 1 | 2 casa quando qualquer alternativa casa, e toda alternativa deve vincular os mesmos nomes. A desestruturação case [a, b] casa apenas uma lista/tupla de exatamente dois elementos; adicione *rest para permitir mais.
Um exemplo mais completo
enum Color { RED, GREEN, BLUE }
def describe(value) -> str {
match value {
case 0 { return "zero" }
case Color.RED { return "red" }
case [a, b] { return f"pair summing to {a + b}" }
case [first, *rest] { return f"list starting with {first}" }
case "hello" { return "the greeting" }
case _ { return "something else" }
}
return ""
}
zero
pair summing to 3
list starting with 1
the greeting
red
something else
A ordem importa: padrões são testados de cima para baixo e o primeiro que casa vence. Coloque os padrões mais específicos primeiro, e termine com case _ quando o domínio não é exaustivo.
Pegadinha: um padrão de binding nu (case s) casa qualquer valor, então um guard sobre ele como case s if s.length() > 3 também será alcançado por valores que não têm length — ordene os cases concretos primeiro, ou cheque o tipo no guard.
Exercícios
- Escreva
def sign(n) -> strusando padrões de literal e de guard. - Faça match sobre uma lista e retorne sua cabeça, ou
nonequando vazia. - Construa um enum de dois valores e despache sobre ele, depois remova
case _e observe o warningE109deaura check.
O que você aprendeu
matchcomo statement e como expressão.- Padrões wildcard, literal, binding, guard, or, enum e desestruturação.
- Sem fallthrough; corpos
->e{ }; a pegadinha dobreak;E109.