Patterns
Patterns appear in match and catch arms, local destructuring, for bindings, and other positions that bind or inspect a value.
Wildcard
Section titled “Wildcard”_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.
Binding identifier
Section titled “Binding identifier”valueA bare identifier normally binds the matched value. A bare identifier that resolves to a nullary constructor is treated as that constructor rather than as a new binding.
Literal patterns
Section titled “Literal patterns”03.14"ready"truefalseInteger, floating-point, string, and boolean literals can be used as literal patterns.
Nullary constructor path
Section titled “Nullary constructor path”Option::NoneStatus::ReadyA qualified path selects the named constructor.
Constructor payload pattern
Section titled “Constructor payload pattern”Option::Some(value)Result::Err(error)Message::Move(x, y)The patterns inside parentheses correspond to the constructor’s payload positions.
Record pattern
Section titled “Record pattern”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 :.
Tuple pattern
Section titled “Tuple pattern”(left, right)(x, y, z)Tuple patterns may nest other patterns:
(Result::Ok(value), _)Patterns in match
Section titled “Patterns in match”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.
Match guards
Section titled “Match guards”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 => ExprThe guard runs only after the pattern itself matches.
Patterns in let
Section titled “Patterns in let”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.
Patterns in for
Section titled “Patterns in for”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.
Patterns in catch
Section titled “Patterns in catch”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.
Nesting
Section titled “Nesting”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.2.0built frombd964738afb6on 2026-10-17 20:41ZVersioningSecurityContributingReleasesSource