Destructuring
Destructuring binds several names at once by matching the shape of an array, struct, or enum — replacing a sequence of individual field accesses with a single pattern.
Array destructuring (tuple pattern) extracts elements by position directly in
the let declaration. In Zolo, tuples are represented as arrays, so
let (a, b, c) = [1, 2, 3] works for any array:
let (lo, hi) = min_max([...]) — destructures a function's return value into two names.
// Feature: Array destructuring (tuple-pattern)
// Syntax: `let (a, b, c) = [v1, v2, v3]`
// When to use: extract multiple values in one line, simulate
// "tuples" on the value side (in Zolo, tuples are arrays).
// Destructure directly from a literal.
let (a, b, c) = [1, 2, 3]
print(a) // 1
print(b) // 2
print(c) // 3
// Heterogeneous types — works because arrays are dynamic.
let (name, age, active) = ["Alice", 30, true]
print(name) // Alice
print(age) // 30
print(active) // true
// Destructure from a function returning an array.
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
// In loops — destructure each element.
let pairs = [[1, "one"], [2, "two"], [3, "three"]]
for pair in pairs {
let (n, label) = pair
print("{n}={label}")
}
// expected:
// 1=one
// 2=two
// 3=three
Struct destructuring happens via match (or if let). The Type { field }
pattern binds the field to a local name; .. ignores remaining fields;
field: new_name renames during extraction:
Extracting x and y from Point; renaming fields; ignoring the rest with ..
// Feature: Struct destructuring via `match`
// Syntax: `match value { Type { field, other } => ... }`
// When to use: extract multiple fields from a struct in a single
// pattern. In Zolo, struct destructuring happens via `match`
// (or `if let`), not directly in `let`.
struct Point {
x: int,
y: int,
}
struct User {
name: str,
age: int,
email: str,
}
// Basic destructure — bindings have the same name as the fields.
let p = Point { x: 3, y: 4 }
match p {
Point { x, y } => print("x={x} y={y}"),
}
// expected: x=3 y=4
// Rename fields during destructure: `field: new_name`.
match p {
Point { x: px, y: py } => print("px={px} py={py}"),
}
// expected: px=3 py=4
// Ignore fields with `..` (rest pattern).
let u = User { name: "Alice", age: 30, email: "[email protected]" }
match u {
User { name, .. } => print("name={name}"),
}
// expected: name=Alice
// `if let` — destructure when the pattern matches.
if let User { email, .. } = u {
print("email={email}")
}
// expected: [email protected]
// Multiple fields extracted.
match u {
User { name, age, .. } => print("{name} ({age})"),
}
// expected: Alice (30)
Enum destructuring uses the same match, with the variant in dot notation
(Enum.Variant(x)). Each variant becomes a separate arm; if let handles the
fast path when only one variant matters:
Result.Ok(v) and Result.Err(e) as match arms; if let for the happy path.
// Feature: Enum destructuring via `match` / `if let`
// Syntax: `match v { Enum.Variant(x) => ... }`
// When to use: extract the payload of an enum variant (Result.Ok,
// Option.Some, events with data, etc.). In Zolo, variants use
// dot-syntax: `MyEnum.Variant`, not `MyEnum::Variant`.
use std::Option
use std::Result
enum Result {
Ok(int),
Err(str),
}
enum Shape {
Circle(float),
Rectangle(float, float),
Point,
}
// Destructure each variant with its payload.
let r1 = Result.Ok(42)
let r2 = Result.Err("not found")
match r1 {
Result::Ok(v) => print("ok={v}"),
Result::Err(e) => print("err={e}"),
}
// expected: ok=42
match r2 {
Result::Ok(v) => print("ok={v}"),
Result::Err(e) => print("err={e}"),
}
// expected: err=not found
// Variants with multiple positional fields.
let s = Shape.Rectangle(10.0, 5.0)
match s {
Shape::Circle(r) => print("circle r={r}"),
Shape::Rectangle(w, h) => print("rect {w}x{h}"),
Shape::Point => print("point"),
}
// expected: rect 10x5
// `if let` — fast path for a single variant.
if let Result::Ok(v) = r1 {
print("got {v}")
}
// expected: got 42
// `if let` with `else` — useful when it does not match.
if let Result::Ok(v) = r2 {
print("got {v}")
} else {
print("no value")
}
// expected: no value
Challenge
Write a function describe_shape(s: Shape) -> str that destructures Shape and
returns a descriptive string for each variant. Use match with payload
destructuring.
See also