-
-
Notifications
You must be signed in to change notification settings - Fork 0
Pattern Language
A pattern asks whether a value has a particular shape and, on success, assigns names to selected
parts of that value. The pattern vocabulary is used by if let, let … else, while let, for,
and match; each construct decides whether failure is permitted. Ordinary let binds a name and
does not provide general destructuring.
| Form | Meaning |
|---|---|
_ |
Accept and discard any value. |
name |
Accept and bind the whole value. |
42, true, 'x', "ok"
|
Require an equal literal. |
(left, right) |
Decompose a tuple. |
{x, y: other} |
Decompose record fields. |
Some(value) |
Require and decompose a variant. |
| `left | right` |
Patterns do not execute arbitrary expressions. A guard belongs to a match arm and runs only
after its structural pattern succeeds.
An irrefutable pattern must succeed for every value of its checked type. Names, wildcards, and a tuple pattern against a known tuple are typical examples. Refutable-control forms reject them because there is no meaningful failure path.
match entry {
case (name, score) => record(name, score)
}
A variant or literal pattern may fail. It therefore requires a construct that states the failure path.
if let Some(score) = selected {
record(score)
}
let Some(required) = selected else { return Err("missing score") }
Using an irrefutable pattern with if let, let … else, or while let is rejected: a condition
that can never fail obscures an ordinary binding, a direct match, or an unconditional loop.
Pattern names exist only where success has been established.
-
if let: the success block. -
while let: the loop body for that iteration. -
for: the loop body for that element. -
match: the selected arm, including its guard. -
let … else: the rest of the containing block after the declaration.
An else block of if let cannot use the success bindings. The subject is evaluated once for
if let and match, and before every attempted iteration for while let.
Alternatives are parsed with | and work when they introduce no bindings.
match response {
case Accepted(_) | Cached(_) => "available"
case Rejected(reason) => report(reason)
}
The language rule requires both sides of an alternative to introduce the same names with compatible types. The current resolver does not implement that rule correctly: repeating the same binder in both alternatives is reported as a duplicate declaration, while different binders may pass checking and leave the unchosen name undefined at run time. Binder-carrying alternatives are therefore not usable correctly in the current implementation. Alternatives with different names remain invalid by design because the arm body cannot have one stable environment.
match value {
case Some(found) if found > 0 => found
case Some(_) => 0
case None => 0
}
Arms are considered from top to bottom. A guard is evaluated only after the pattern succeeds. A
guarded arm never proves exhaustiveness because the guard may be false. The unguarded Some(_)
arm above is therefore semantically necessary, not redundant.
The checker proves coverage for closed sums and booleans. A missing case is an error. An arm fully covered by earlier unguarded arms is unreachable and produces a warning. A wildcard is useful at an intentionally open boundary, but explicit variants retain better compiler help when a closed sum evolves.
- Modules And Imports
- Values, Bindings, And Blocks
- Types And Inference
- Records, Sums, And Tuples
- Functions Generics And Traits
- Pattern Language
- Control Flow And Patterns
- Iteration And Loops
- Failure And Propagation
- Numbers And Collections
- Sets Maps And Sequences
- Keyed Structures
- Trees And Hierarchies
- Standard Library
- Output Formatting And Testing
- Tasks And Scopes
- Compile-Time Evaluation
- Typed Macros
- References And Unsafe
- Worked Programs