Desestruturação: Tuplas, Arrays, Structs e Enums
Nesta página
Padrões não servem apenas para literais: eles podem penetrar dentro de estruturas e extrair valores em um único passo, sem acesso manual por índice ou campo.
Tuplas e arrays em let
Em Zolo, a desestruturação posicional usa a forma let (a, b, c) = [...].
Os nomes recebem os valores na ordem, e o compilador verifica o tamanho.
Funciona em let simples, em for e ao receber retornos de funções.
Desestruturação posicional em let, retorno de função e laço for.
// Feature: Tuple / array destructuring in `let`
// Syntax: `let (a, b, c) = [v1, v2, v3]`
// When to use: extract multiple values positionally. In Zolo,
// "tuples" at the value site are arrays.
// Basic form: 3 named values.
let (a, b, c) = [1, 2, 3]
print(a) // 1
print(b) // 2
print(c) // 3
// Heterogeneous types: each slot can be a different type.
let (name, age, active) = ["Alice", 30, true]
print(name) // Alice
print(age) // 30
print(active) // true
// Destructuring a function returning an array — min/max pattern.
fn min_max(arr: [int]) -> [int] {
var lo = arr[0]
var hi = arr[0]
for x in arr {
if x < lo { lo = x }
if x > hi { hi = x }
}
return [lo, hi]
}
let (lo, hi) = min_max([3, 1, 4, 1, 5, 9, 2, 6])
print("lo={lo} hi={hi}") // lo=1 hi=9
// Destructuring inside `for`.
let pairs = [[1, 2], [3, 4], [5, 6]]
for pair in pairs {
let (l, r) = pair
print("{l},{r}")
}
// expected:
// 1
// 2
// 3
// Alice
// 30
// true
// lo=1 hi=9
// 1,2
// 3,4
// 5,6
Padrões de array com ..
Além de destruturar um número fixo de elementos, você pode separar o primeiro
(ou últimos) elemento(s) do resto com ..rest. A rest é um novo array.
O .. sem nome simplesmente ignora os elementos restantes e serve para
verificar a forma do array.
[first, ..rest], [a, b, ..], array vazio e checagem de comprimento mínimo.
// Feature: Array patterns with rest `..`
// Syntax: `[first, ..rest]`, `[a, b, ..]`, `[..init, last]`
// When to use: separate the first/last element(s) from the "rest"
// without index arithmetic.
let arr = [1, 2, 3, 4, 5]
// First + rest.
match arr {
[first, ..rest] => print("first={first} rest_len={rest.len()}"),
_ => print("empty"),
}
// First two + rest.
match arr {
[a, b, ..rest] => print("a={a} b={b} rest_len={rest.len()}"),
_ => print("fewer than 2"),
}
// Without naming the rest — only checks shape.
match arr {
[_, _, ..] => print("has at least 2"),
_ => print("does not have 2"),
}
// Empty array.
let empty: [int] = []
match empty {
[] => print("really empty"),
[_, ..] => print("has at least 1"),
}
// expected:
// first=1 rest_len=4
// a=1 b=2 rest_len=3
// has at least 2
// really empty
Desestruturação de structs
Dentro de um match, o padrão TipoStruct { campo1, campo2, .. } extrai
campos pelo nome. O .. ignora os campos restantes sem precisar listá-los.
Guardas podem ser adicionadas depois do padrão para refinar a seleção.
Extraindo campos de Point e Person; guarda sobre campo extraído.
// Feature: Struct destructuring
// Syntax: `Type { field1, field2, .. }`
// When to use: extract specific fields from a struct inside a
// match or let. The `..` ignores the remaining fields.
struct Point {
x: float,
y: float,
z: float,
}
let p = Point { x: 1.0, y: 2.0, z: 3.0 }
// Take everything by name.
match p {
Point { x, y, z } => print("({x},{y},{z})"),
}
// Just some fields — `..` ignores the rest.
match p {
Point { x, .. } => print("only x = {x}"),
}
match p {
Point { y, z, .. } => print("y={y} z={z}"),
}
// Direct struct destructure in `let` is not yet supported in
// pattern position — use `match` when you need a full pattern,
// or field access (`p.x`) for the simple case.
print("p.x = {p.x}")
print("p.y = {p.y}")
// Match with guard on fields.
struct Person {
name: str,
age: int,
}
let alice = Person { name: "Alice", age: 17 }
let label = match alice {
Person { age, .. } if age >= 18 => "adult",
Person { age, .. } => "minor ({age})",
}
print(label) // minor (17)
// expected:
// (1,2,3)
// only x = 1
// y=2 z=3
// p.x = 1
// p.y = 2
// minor (17)
Nota: a desestruturação direta de struct em
letainda não é suportada em posição de padrão — usematchou acesso por campo (p.x) nesses casos.
Desestruturação de enums
Cada variante de um enum pode carregar payload (posicional ou com campos
nomeados). O padrão extrai esse payload diretamente. O match sobre um enum
é exaustivo por construção: o compilador exige um braço para cada variante.
Shape com variantes tuple e struct; Event com payload nomeado.
// Feature: Enum destructuring (with payload)
// Syntax: `Enum.Variant(x, y)` or `Enum.Variant { field }`
// When to use: extract values from each variant in a match.
enum Shape {
Circle(float),
Rect(float, float),
Triangle {
a: float,
b: float,
c: float,
},
Empty,
}
fn area(s: Shape) -> float {
return match s {
Shape::Circle(r) => 3.14159 * r * r,
Shape::Rect(w, h) => w * h,
Shape::Triangle { a, b, c } => {
let semi = (a + b + c) / 2.0
return semi
},
Shape::Empty => 0.0,
}
}
print(area(Shape.Circle(5.0))) // 78.53975
print(area(Shape.Rect(3.0, 4.0))) // 12
// Struct-like variant constructor uses `::`.
print(area(Shape::Triangle { a: 3.0, b: 4.0, c: 5.0 })) // 6
print(area(Shape.Empty)) // 0
// Enum with struct-like variants — named-field pattern.
enum Event {
Click {
x: int,
y: int,
},
Key(str),
Quit,
}
fn describe(e: Event) -> str {
return match e {
Event::Click { x, y } => "click at ({x},{y})",
Event::Key(k) => "key {k}",
Event::Quit => "quit",
}
}
print(describe(Event::Click { x: 100, y: 200 }))
print(describe(Event.Key("Enter")))
print(describe(Event.Quit))
// expected:
// 78.53975
// 12
// 6
// 0
// click at (100,200)
// key Enter
// quit
Desafio
Adicione uma nova variante Point(float, float) ao enum Shape do exemplo
de enums e trate-a na função area. Observe como o compilador avisa sobre
exaustividade antes de você adicionar o braço.
Veja também