Skip to content
You are reading v0.3, which is not the current release. Go to v0.4.

Patterns

Patterns appear in match and catch arms, local destructuring, for bindings, and other positions that bind or inspect a value.

_

The wildcard matches a value without binding it. Prefer an explicit arm per variant where the cases mean different things: a wildcard earns its place when the remaining cases genuinely share a behaviour, and costs something when they do not, because it is the one arm a newly added variant will silently fall into.

value

A bare identifier always binds the matched value, even when a nullary constructor has the same name. Ready => 1 binds every value to a new variable called Ready; it does not match the Ready case. Qualify a constructor pattern — Status::Ready => 1 — and it matches the case.

The compiler catches the common shape of this mistake: a bare name before other arms makes those arms unreachable, and that is an error naming the fix. It cannot catch every shape. A bare name as the only arm, or as the last one, leaves nothing unreachable, so the match compiles and silently binds everything — with a `Ready` is bound and never read warning as the one signal. Treat that warning as an error in a match over a variant type.

0
3.14
"ready"
true
false

Integer, floating-point, string, and boolean literals can be used as literal patterns.

Option::None
Status::Ready

A qualified path selects the named constructor.

Option::Some(value)
Result::Err(error)
Message::Move(x, y)

The patterns inside parentheses correspond to the constructor’s payload positions.

Shorthand field binding:

User { id, name }

Explicit nested pattern:

User {
id: user_id,
name: "admin",
}

General shape:

Path {
field,
field: Pattern,
...
}

A record pattern begins with a path and may bind fields by shorthand or supply another pattern after :.

(left, right)
(x, y, z)

Tuple patterns may nest other patterns:

(Result::Ok(value), _)
match result {
Result::Ok(value) => use_value(value),
Result::Err(error) => handle(error),
}

The compiler checks exhaustiveness and unreachable arms. Adding a variant to a type therefore turns every exhaustive match on it into a compile error, which is the list of places that need a decision about the new case.

A guard belongs to the arm after the pattern:

match score {
value if value >= 90 => "high",
value if value >= 50 => "medium",
_ => "low",
}

General form:

Pattern if BoolExpr => Expr

The guard runs only after the pattern itself matches.

let (left, right) = pair;
let User { id, name } = user;

A pattern used directly by let must be valid for the value’s type without requiring a missing-case branch. Refutable alternatives belong in match.

for entry in Dict::entries(table) {
print("${entry.key}: ${entry.value}");
}

The pattern binds each yielded item, under the same rules as let: valid for the item’s type, and irrefutable. Dict::entries yields a Pair, which is a record — a tuple pattern such as (key, value) is only valid where the item’s type is a tuple, and is refused otherwise.

for needs Step and Iterator in scope, and Pair too if the body reads the item’s fields: import std::core::{Dict, Iterator, Pair, Step, print};. See Control flow.

load_user(id)! catch {
UserError::NotFound(missing_id) => fallback(missing_id),
UserError::Unavailable(reason) => offline(reason),
}

catch reuses the pattern syntax but adds failure-row semantics: exhaustively handling a failure type removes that type from the failures that can leave the expression.

Patterns compose recursively:

match value {
Envelope {
payload: Result::Ok(User { id, name }),
} => use_user(id, name),
_ => fallback(),
}

Use nesting when it makes the shape clearer; split deeply nested business decisions into smaller matches when a single arm becomes difficult to read.

See Control flow for match result rules and Failures for catch semantics.

Khora 0.3.0built frombd964738afb6on 2026-10-17 20:41ZVersioningSecurityContributingReleasesSource