Switch Statements
switch provides ordered branching over either one subject value
or a sequence of boolean conditions.
switch status {
case Status.Ready:
Start()
case Status.Waiting:
Wait()
default:
Stop()
}
Sec supports both subject-based and subjectless switches. In both forms, cases are considered in source order and the first matching case wins.
switch is ordered branching, not structural
pattern matching. Use match when variants must be destructured,
payloads bound, exhaustiveness required, or a branch expression must produce a value.
Subject Switch
A subject switch evaluates one expression and compares cases against it:
switch expression {
case value:
statements
case value1, value2:
statements
case range:
statements
case < limit:
statements
default:
statements
}
The body may contain zero or more case clauses and one optional
final default clause.
Case items may be ordinary compatible values, ranges or relational forms.
Subjectless Switch
Omitting the subject creates an ordered chain of boolean conditions:
switch {
case score < 0:
Invalid()
case score < 50:
Failed()
case score < 80:
Passed()
default:
Excellent()
}
Every case item in a subjectless switch must have type bool.
There is no implicit truthiness.
A subjectless switch is useful when several peer conditions are clearer
than a long if / else if chain.
Subject Evaluation
The subject expression is evaluated exactly once.
switch ReadStatus() {
case Status.Ready:
Start()
default:
Stop()
}
ReadStatus() is called once. The resulting value is then used
for all case tests.
The compiler may keep that value in an SSA value, machine register or temporary storage, but lowering must preserve the one-evaluation source semantics.
Case Evaluation Order
Cases are considered from top to bottom in source order.
Within one case, comma-separated alternatives are considered left to right.
switch value {
case first, second, third:
Selected()
case other:
Other()
}
The first matching case body executes.
After a case matches:
- later case tests are not evaluated;
- the selected case body executes once;
- normal completion continues after the switch;
- explicit
fallthroughmay instead enter the next case body.
Value Cases
A value case compares the subject using ordinary Sec equality semantics.
switch value {
case 1:
One()
case 2, 3, 5, 7:
Prime()
default:
Other()
}
Comma-separated values are alternatives. The body executes when the subject equals any one of them.
Every case item must be equality-compatible with the subject. A switch does not introduce implicit conversions or relaxed type identity.
type CustomerID uint64
type ProductID uint64
let customer: CustomerID := 42
let product: ProductID := 42
switch customer {
case product:
... // invalid
}
Dynamic Case Expressions
Case values are not restricted to compile-time constants.
switch value {
case minimum:
AtMinimum()
case CalculateTarget():
AtTarget()
}
Case expressions may include:
- literals;
- constants;
- variables;
- fields;
- properties;
- function calls;
- method calls;
- other expressions valid for equality comparison with the subject.
Dynamic case expressions are evaluated lazily only when execution reaches them.
switch value {
case KnownValue:
HandleKnown()
case ExpensiveCheckValue():
HandleExpensive()
}
If KnownValue matches, ExpensiveCheckValue() is not called.
Range Cases
A subject switch may test range membership directly:
switch score {
case 0..<50:
Failed()
case 50..<80:
Passed()
case 80..100:
Excellent()
}
Range cases use the ordinary Sec range forms:
start..end
start..<end
start..
..end
..<end
The subject and the range bounds must have compatible ordered types.
A range case is a membership test. It does not construct a sequence, iterate over the range or reevaluate the subject.
Relational Cases
A subject switch may use an ordered comparison without repeating the subject:
switch value {
case < 0:
Negative()
case 0:
Zero()
case > 0:
Positive()
}
Supported forms are:
case < expression:
case <= expression:
case > expression:
case >= expression:
This:
switch value {
case < limit:
Below()
}
uses the same ordered-comparison semantics as:
if value < limit {
Below()
}
The subject and the relational operand must support the requested ordered comparison.
Several relational alternatives
switch value {
case < minimum, > maximum:
Outside()
default:
Inside()
}
These alternatives mean OR, not conjunction. They are evaluated left to right.
Mixed Alternatives
A subject case may combine compatible values, ranges and relational items:
switch value {
case 1, 3, 5, 10..<20:
Selected()
default:
Other()
}
Each item is tested left to right. The body executes once when any item matches.
Every alternative must independently satisfy the type rules required by its form.
Subjectless Comma Alternatives
In a subjectless switch, comma-separated conditions behave like boolean OR with left-to-right short-circuit evaluation:
switch {
case user.IsAdmin(), user.IsOwner():
Allow()
case user.IsBlocked():
Deny()
default:
Review()
}
Once one condition in a case becomes true, later alternatives in
that case are skipped.
Once a case matches, every later case is skipped.
Default
default executes only when no preceding case matches.
switch value {
case 1:
One()
case 2:
Two()
default:
Other()
}
default is optional.
If no case matches and no default exists, execution continues
after the switch.
A switch may contain at most one default, and it must be the final
clause.
No Implicit Fallthrough
Ordinary cases do not continue into the following case.
switch value {
case 1:
One()
case 2:
Two()
}
When value is 1, only One() executes.
Normal completion then continues after the switch.
There is no required break at the end of a normal case.
Explicit fallthrough
Use fallthrough when execution must continue directly into the
next case body:
switch value {
case 1:
One()
fallthrough
case 2:
OneOrTwo()
default:
Other()
}
When value is 1:
One()executes;fallthroughtransfers directly to the next case body;- the next case's tests are not evaluated;
OneOrTwo()executes.
fallthrough must:
- be the final non-empty statement in its case;
- have a following case;
- appear directly in the case body;
- not appear in
default.
A nested if, loop, lambda or other nested block cannot use
fallthrough to target an enclosing switch case.
Empty Cases
An empty case is legal:
switch value {
case 1:
case 2:
Two()
}
An empty case does not implicitly fall through. When value is
1, no statements execute and control continues after the switch.
Use explicit fallthrough if the next case body should execute.
Case Scope
Every case body has its own lexical scope.
switch value {
case 1:
let message := "one"
Print(message)
case 2:
let message := "two"
Print(message)
}
The two message bindings belong to independent sibling scopes.
A binding declared in one case is not visible in another case or after the switch.
fallthrough transfers control into the next body, but it does not
merge the lexical scopes:
switch value {
case 1:
let local := 10
fallthrough
case 2:
Use(local) // invalid
}
Ownership, destruction and borrow analysis still account for the control-flow
edge created by fallthrough.
Duplicate and Overlapping Cases
Sec diagnoses case selections that are provably duplicated or unreachable.
Duplicate compile-time values
switch value {
case 1:
First()
case 1:
Second()
}
This is invalid. The compiler does not silently rely on source order when two compile-time values are known to be equal.
Overlapping compile-time ranges
switch score {
case 0..50:
First()
case 40..100:
Second()
}
The ranges overlap and are invalid when the overlap is statically known.
A later exact value cannot be hidden inside an earlier compile-time range:
switch score {
case 0..100:
InRange()
case 50:
Exact()
}
Relational reachability
Partial overlap is valid when later values remain reachable:
switch value {
case <= -10:
VeryLow()
case < 0:
Low()
}
The second case still handles values from -9 through -1.
A fully covered later relational case is invalid:
switch value {
case >= 0:
NonNegative()
case > 10:
Large()
}
Dynamic overlap
If values or bounds are dynamic and the compiler cannot prove complete coverage, the cases remain valid. Runtime source order determines which matching case wins.
Enum Subjects
An enum may be used as a switch subject:
switch direction {
case Direction.North:
North()
case Direction.South:
South()
default:
Other()
}
A switch over an enum is not required to be exhaustive.
Use match when exhaustive handling of the enum domain is part
of the intended semantics.
For open foreign or hardware enum domains, the owning enum or FFI rules may
require handling unknown values, often through default.
String Subjects
Strings may be switched on using ordinary string equality:
switch command {
case "start":
Start()
case "stop":
Stop()
default:
Unknown()
}
switch does not introduce a special case-insensitive, locale-sensitive
or otherwise altered string comparison.
Named Semantic Types and Units
Named types retain their ordinary compatibility rules inside a switch.
switch speed {
case minimumSpeed..maximumSpeed:
Normal()
}
An unrelated primitive value does not become compatible merely because it is written in a case.
Unit-bearing values retain normal unit compatibility:
switch distance {
case 0<m>..<100<m>:
Near()
case 100<m>..:
Far()
}
Incompatible units cannot participate in the same equality, range or ordered
comparison merely because the operation occurs inside switch.
Boolean Subjects
A boolean value may be used as an explicit subject:
switch ready {
case true:
Start()
case false:
Wait()
}
This is valid Sec. An if / else is often simpler
when there is only one boolean distinction, but the language does not prohibit
a boolean subject switch.
Switch Is Not Pattern Matching
switch does not destructure union, enum, Result or
Option payloads.
Invalid:
switch result {
case Ok(value):
Use(value)
}
Use match:
match result {
Ok(value) => Use(value)
Err(error) => Handle(error)
}
switch does not add:
- wildcard patterns;
- variant payload bindings;
- property patterns;
- list patterns;
- type-binding patterns;
- structural destructuring.
switch for ordered tests.
Use match for structural patterns, payloads and exhaustiveness.
No Switch Case Guards
Sec 0.1 does not add a separate where guard to switch cases.
Invalid:
switch value {
case 10..20 where user.Active:
Handle()
}
Use a subjectless switch when the complete boolean condition should select the case:
switch {
case value in 10..20 && user.Active:
Handle()
}
or place an if inside the selected case body.
where belongs to the guarded-pattern model of match,
not to switch.
Switch Is a Statement
switch does not produce a value.
Invalid:
let result := switch value {
case 1:
10
default:
20
}
Use match when the branch construct itself should produce a value,
or assign to an outer mutable binding:
let mut result: int
switch value {
case 1:
result = 10
default:
result = 20
}
Definite-assignment analysis verifies that every continuing path has assigned the value before it is read.
Break and Continue
A switch does not create a break or continue target.
Those statements retain their loop-only meaning.
switch value {
case 1:
break // invalid unless this statement belongs to an applicable enclosing loop context
}
Ordinary switch cases do not need break because there is no
implicit fallthrough.
fallthrough is the only switch-specific case-to-case transfer statement.
Flow Analysis
Switch cases participate in ordinary path-sensitive control-flow, ownership, borrowing, cleanup and definite-assignment analysis.
Terminating switch
A switch terminates when every reachable execution path through it terminates rather than reaching the continuation after the statement.
fn Classify(value: int) int {
switch value {
case < 0:
return -1
case 0:
return 0
default:
return 1
}
}
This satisfies the function return requirement because every possible switch path returns.
A switch without complete coverage normally retains an unmatched path that continues after the statement.
Definite assignment
fn Select(value: int) int {
let mut result: int
switch value {
case 1:
result = 10
case 2:
result = 20
default:
result = 30
}
return result
}
This is valid because every continuing path assigns result.
Without complete coverage, the possible unmatched path must also be included in definite-assignment analysis.
Fallthrough chains
A fallthrough edge participates in the same flow merge, ownership,
destruction and cleanup reasoning as other explicit control-flow edges.
Unreachable cases
When earlier cases provably cover every value that could reach a later case, the later case is unreachable and the compiler diagnoses it.
Lowering Semantics
The backend may choose different low-level representations for different switches. A constant integer switch may become a jump table, while another switch may become a decision tree or ordinary branches.
Every lowering strategy must preserve:
- one evaluation of the subject;
- source-order case testing where evaluation is observable;
- left-to-right comma-alternative evaluation;
- short-circuiting for subjectless comma alternatives;
- first-match-wins semantics;
- no implicit fallthrough;
- direct body transfer for explicit
fallthrough; defaultonly after all earlier tests fail;- case lexical scopes, destruction and cleanup edges.
Best Practices
Use switch for ordered peer tests
A switch is useful when several comparisons against one subject or several peer boolean conditions read more clearly than nested conditional code.
Prefer ranges for interval meaning
switch score {
case 0..<50:
Failed()
case 50..<80:
Passed()
default:
Excellent()
}
This usually communicates interval intent more directly than repeating lower and upper comparisons.
Prefer relational cases when they avoid repeated subject syntax
switch value {
case < 0:
Negative()
case 0:
Zero()
case > 0:
Positive()
}
Use fallthrough sparingly
Use fallthrough only when the next case body must execute
without reevaluating the next case's tests.
Do not use it to recreate C-style switch behavior.
Use match when structure matters
Prefer match when:
- exhaustiveness is part of the intended contract;
- variants or payloads must be bound;
- structural patterns are required;
- guards belong to patterns;
- the branch construct should produce a value.
Summary
switchsupports subject-based and subjectless forms.- A subject expression is evaluated exactly once.
- Cases are tested from top to bottom in source order.
- Comma-separated alternatives are tested left to right.
- The first matching case wins.
- Value cases use ordinary equality compatibility.
- Case expressions may be dynamic and are evaluated lazily when reached.
- Range cases use ordinary Sec range and ordered-type semantics.
- Relational cases use
<,<=,>and>=without repeating the subject. - Value, range and relational alternatives may be combined when compatible.
- Every subjectless case item must have type
bool. - Subjectless comma alternatives short-circuit like ordered OR alternatives.
defaultis optional, unique and final.- There is no implicit case fallthrough.
- Explicit
fallthroughenters the immediately following case body without testing that case. - An empty case does not imply fallthrough.
- Every case body has its own lexical scope.
- Fallthrough does not merge case scopes.
- Duplicate compile-time values are invalid.
- Provably overlapping compile-time ranges are invalid.
- Fully covered later cases are unreachable and diagnosed.
- Dynamic overlap remains valid when complete coverage cannot be proven.
- Enums, strings, booleans, named semantic types and unit-bearing values may be switch subjects when their ordinary operations permit it.
switchdoes not provide general pattern matching.- Switch case
whereguards are not part of Sec 0.1. switchis a statement and does not produce a value.breakandcontinueremain loop-control statements.- Switch termination and definite assignment are path-sensitive.
- Backend optimizations must preserve source-order and first-match semantics where observable.