Patrones en Instrucciones: if let, while let, let else y match-expresión
En esta página
Los patrones en Zolo no están restringidos al match. Aparecen en cuatro
otras formas sintácticas, cada una con un propósito distinto.
if let: coincidir con una sola variante
Cuando solo te importa una variante del enum (típicamente Some/Just/Ok),
if let es más conciso que un match de dos ramas. El else es opcional.
if let con y sin else; encadenamiento 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: bucle guiado por patrón
while let se repite mientras el patrón coincida y se detiene cuando falla.
Es el idioma natural para consumir una pila, cola o iterador que señala
el fin con None/Nothing.
Nota:
while lettiene soporte parcial en el verificador de tipos en algunas configuraciones. El ejemplo usa la forma equivalenteloop { match ... }que es segura en todos los casos.
Drenado de pila con 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: cláusula de guarda con binding
let else es el idioma para extraer un valor que esperas que esté ahí,
con una salida limpia en caso contrario. El bloque else debe divergir
(return, panic, break). Después de la línea, los nombres del patrón están
disponibles en el ámbito normal — sin indentación extra.
let else con enum, con patrón de array y encadenado como cláusulas de guarda.
// 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 expresión
match es una expresión: el valor de la rama elegida es el valor del match
completo. Puede aparecer en el lado derecho de un let, dentro de una
expresión aritmética, en interpolación de string o como cuerpo de función.
match como RHS de let, dentro de una expresión aritmética y como retorno de función.
// 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
Desafío
Reescribe la función classify del último ejemplo usando if let en lugar de
match. ¿Qué versión te parece más legible para ese caso?
Consulta también