Padrões em Instruções: if let, while let, let else e match-expressão
Nesta página
Padrões em Zolo não estão restritos ao match. Eles aparecem em quatro
outras formas sintáticas, cada uma com um propósito distinto.
if let: casar uma única variante
Quando você só se importa com uma variante do enum (tipicamente Some/Just/Ok),
if let é mais conciso que um match de dois braços. O else é opcional.
if let com e sem else; encadeamento de if let como despacho de variante.
// Feature: `if let` — match a single variant
// Syntax: `if let Pat = expr { ... } else { ... }`
// When to use: you only care about ONE of the enum's variants
// (typically `Some`/`Just`/`Ok`) and want a simple else for the
// rest. More concise than a two-arm `match`.
enum Opt {
Some(int),
None,
}
let val = Opt::Some(42)
if let Opt::Some(x) = val {
print("got: {x}") // got: 42
} else {
print("none")
}
// No else — only runs if matched.
let empty = Opt::None
if let Opt::Some(x) = empty {
print("got: {x}")
}
print("after if let") // after if let
// Chained `if let` for different variants.
enum Shape {
Circle(float),
Square(float),
Empty,
}
fn area(s: Shape) -> float {
if let Shape::Circle(r) = s {
return 3.14159 * r * r
}
if let Shape::Square(side) = s {
return side * side
}
return 0.0
}
print(area(Shape::Circle(5.0))) // 78.53975
print(area(Shape::Square(4.0))) // 16
print(area(Shape::Empty)) // 0
while let: laço guiado por padrão
while let repete enquanto o padrão casar e encerra quando ele falha.
É o idioma natural para consumir uma pilha, fila ou iterador que sinaliza
fim com None/Nothing.
Nota: o
while lettem suporte parcial no verificador de tipos em algumas configurações. O exemplo usa a forma equivalenteloop { match ... }que é segura em todos os casos.
Drenagem de pilha com loop { match ... } — equivalente a while let.
// Feature: `while let` — loop while a pattern matches
// Syntax: `while let Pat = expr { ... }`
// When to use: consume an iterator / pop a stack / drain a queue —
// anything that returns `Some`/`Just` while items remain and
// `None`/`Nothing` once empty.
//
// Note: the binding inside `while let` still has partial type
// checker support. To pass `zolo check`, use the equivalent
// `loop { match ... }` form shown below. The `while let` form is
// preferred once stable.
use std::Array
enum Opt {
Some(int),
None,
}
// Manual stack with pop. Array slicing via `arr[a..b]` is supported
// (returns a new array). `Array.pop` is used here for in-place removal.
struct MyStack {
items: [int],
}
impl MyStack {
fn from(items: [int]) -> MyStack {
return MyStack { items: items }
}
fn take(self) -> Opt {
if self.items.len() == 0 {
return Opt::None
}
let last = self.items.pop()
return Opt::Some(last)
}
}
let s = MyStack::from([1, 2, 3, 4, 5])
// Equivalent to `while let Opt::Some(item) = s.take() { ... }`.
var done = false
while !done {
let p = s.take()
match p {
Opt::Some(item) => print(item),
Opt::None => {
done = true
},
}
}
print("empty")
// expected:
// 5
// 4
// 3
// 2
// 1
// empty
let else: guard clause com binding
let else é o idioma para extrair um valor que você espera estar lá,
com uma saída limpa caso contrário. O bloco else deve divergir
(return, panic, break). Após a linha, os nomes do padrão estão
disponíveis no escopo normal — sem indentação extra.
let else com enum, com padrão de array e encadeado como guardas de cláusula.
// Feature: `let else` — infallible destructuring with diverging fallback
// Syntax: `let Pat = expr else { ... return / break / panic }`
// When to use: extract a value you EXPECT to match, with a clean
// escape hatch when it doesn't. The `else` block must end the
// function (return/panic/etc.). After the line, the pattern's
// names are available in normal scope.
enum Opt {
Some(int),
None,
}
fn double_or_zero(o: Opt) -> int {
let Opt::Some(x) = o else {
return 0
}
// `x` is available from here on.
return x * 2
}
print(double_or_zero(Opt::Some(21))) // 42
print(double_or_zero(Opt::None)) // 0
// let-else with array pattern: take the first or return -1.
fn first(arr: [int]) -> int {
let [head, ..rest] = arr else {
return -1
}
return head
}
print(first([10, 20, 30])) // 10
let empty: [int] = []
print(first(empty)) // -1
// Chaining: several guard clauses in a row.
fn add_first_two(arr: [int]) -> int {
let [a, ..rest] = arr else { return -1 }
let [b, ..rest2] = rest else { return -1 }
return a + b
}
print(add_first_two([3, 4, 5])) // 7
print(add_first_two([3])) // -1
match como expressão
match é uma expressão: o valor do braço escolhido é o valor do match
inteiro. Ele pode aparecer no lado direito de um let, dentro de uma
expressão aritmética, em interpolação de string ou como corpo de função.
match como RHS de let, dentro de expressão aritmética e como retorno de função.
// Feature: `match` as expression — returns a value
// Syntax: `let v = match expr { pat => val, ... }`
// When to use: a match's value is the value of the matched arm. It
// replaces nested ifs when you only need a value derived from a
// classification.
let n = 7
// Can be the RHS of a let.
let parity = match n % 2 {
0 => "even",
_ => "odd",
}
print(parity) // odd
// Can be part of a larger expression.
let pts = 50 + match n {
n if n > 5 => 100,
n if n > 0 => 50,
_ => 0,
}
print(pts) // 150
// Can appear inside interpolation.
let label = match n {
0 => "zero",
1..=5 => "small",
_ => "big",
}
print("n={n} -> {label}") // n=7 -> big
// `match` as a function return — no `return` needed for a terminal
// match.
fn classify(x: int) -> str {
return match x {
n if n < 0 => "negative",
0 => "zero",
_ => "positive",
}
}
print(classify(-3)) // negative
print(classify(0)) // zero
print(classify(42)) // positive
// expected:
// odd
// 150
// n=7 -> big
// negative
// zero
// positive
Desafio
Reescreva a função classify do último exemplo usando if let em vez de
match. Qual versão você considera mais legível para esse caso?