Skip to content

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.

06-destructuring-array.zolo
Playground
// 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 ..

07-destructuring-struct.zolo
Playground
// 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: "a@x.com" }
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=a@x.com

// 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.

08-destructuring-enum.zolo
Playground
// 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.

enespt-br