Basic Patterns: Literals, Wildcard and Binding
Every pattern in Zolo is built from three fundamental primitives. Understanding them is the first step toward mastering the language's pattern system.
Literal patterns
A literal pattern matches when the value is exactly equal to the written literal.
It works with integers, floats, strings, booleans, negatives — any type with
equality. The _ arm at the end makes the match exhaustive.
Integer, string, boolean and negative literals in patterns.
// Feature: Literal patterns — match by exact equality
// Syntax: `match x { 1 => ..., "a" => ..., true => ... }`
// When to use: discriminate by concrete value. Works with ints,
// floats, strings, bools, nil — anything with equality.
let n = 2
let name = match n {
1 => "one",
2 => "two",
3 => "three",
_ => "other",
}
print(name) // two
// String literal.
let cmd = "help"
let response = match cmd {
"help" => "showing help",
"quit" => "exiting",
"version" => "version 1.0",
_ => "unknown command",
}
print(response) // showing help
// Bool literal — exhaustive bool match without needing `_`.
let active = true
let label = match active {
true => "ON",
false => "OFF",
}
print(label) // ON
// Negative literal.
let temp = -5
let kind = match temp {
-10 => "very cold",
-5 => "cold",
0 => "freezing point",
_ => "other",
}
print(kind) // cold
// expected:
// two
// showing help
// ON
// cold
Wildcard _
The wildcard _ matches any value without capturing it. Use it when you
need to cover the remaining case but won't use the value in the arm body.
It is the most efficient way to make a match exhaustive.
_ as a catch-all without creating a variable.
// Feature: Wildcard `_` — matches anything, discards the value
// Syntax: `match x { ... _ => ... }`
// When to use: catch-all that does NOT need the value. Useful to
// make a match exhaustive without naming the variable.
let n = 99
let label = match n {
0 => "zero",
1 => "one",
_ => "other",
}
// doesn't name n
print(label) // other
// Wildcard as a guard against unenumerated values.
let cmd = "shutdown"
let r = match cmd {
"start" => "starting",
"stop" => "stopping",
_ => "ignored",
}
print(r) // ignored
// Difference vs binding: `_` cannot be used in the arm body.
// When you want to read the value, prefer binding (`n =>`).
let x = 10
let kind = match x {
1 | 2 | 3 => "small",
_ => "large",
}
print(kind) // large
// expected:
// other
// ignored
// large
Binding: capturing the value
A binding replaces _ when you need the value in the arm body.
Any identifier (e.g. n, other, name) that is not an enum variant
acts as a binding: it always matches and binds the value to the name.
n => captures the value; other => as a named catch-all.
// Feature: Binding pattern — capture the value in a name
// Syntax: `match x { name => ... }` — any identifier
// When to use: capture the value that matched to use it in the arm.
let x = 42
// Simple binding — `n` receives x. Always matches, so it must come
// last (or be the only arm).
let msg = match x {
n => "got {n}",
}
print(msg) // got 42
// Combined with literals — binding is the catch-all that replaces
// `_` when you need the value.
let y = 7
let label = match y {
1 => "one",
2 => "two",
other => "other ({other})",
}
print(label) // other (7)
// Binding in a string match.
let s = "zolo"
let r = match s {
"rust" => "systems language",
name => "a language called {name}",
}
print(r) // a language called zolo
// expected:
// got 42
// other (7)
// a language called zolo
Challenge
In the binding example, add a 0 => "zero" arm before the binding and
verify the order: the literal arm takes priority over the catch-all.
See also