if / else
The if / else construct provides branching based on a boolean condition.
if is available both as a statement and as a value expression. Both forms
evaluate the condition once and execute only the selected branch.
Surface Syntax#
Minimal form:
if <condition> {
...
}
With an else:
if <condition> {
...
} else {
...
}
Value expression#
A value-position conditional has an expression at the end of each branch:
let label = if ready {
"ready"
} else {
"not ready"
};
An expression form requires else, and both branch values must have one
compatible type. The selected value becomes the result; the other expression
is not evaluated. This contract applies to strings, scalars, optionals,
results, and the other value categories accepted by the checker and hosted
lowerer.
Lowering a value-position conditional is type-directed before it emits either branch. Type classification must not evaluate the condition or leave partial control flow behind. The condition is therefore evaluated exactly once even when the result binding omits an explicit type annotation, and values borrowed by the condition (including string views) remain valid after the conditional.
if let (Pattern-Destructuring Statement Form)#
Silk also supports an if let statement form for refutable pattern matching
without introducing a separate match expression:
if let <pattern> = <scrutinee> {
...
} else {
...
}
if let mut <pattern> = <scrutinee> {
...
}
Notes:
- The scrutinee expression is evaluated exactly once.
- The pattern binders (for example
Some(v)bindsv) are in scope only in thethenblock. if let mutmarks binders introduced by the pattern as mutable in that scope, so they may be reassigned like ordinarylet mutlocals.elseis optional (when omitted, a non-matching scrutinee executes no block).else if let ...chains are supported and parse as nesting in the same way aselse if ....else let ...is supported as shorthand forelse if let ...;else let muthas the same binder mutability aselse if let mut.if let move ...,else if let move ..., andelse let move ...consume the scrutinee for ownership-tracked values. The consumed source binding is not available in thethenblock, theelseblock, or after theif.
if let chains (&& let)#
The if let statement form supports a short-circuiting && chain that mixes
refutable let clauses and ordinary boolean clauses:
if let Some(x) = get_x() &&
x > 0 &&
let mut Ok(v) = get_value(x) {
// `x` and `v` are in scope here.
v = v + 1;
return v;
} else {
// `x` and `v` are NOT in scope here.
return 0;
}
Semantics:
- Clauses are evaluated left-to-right and short-circuit like
&&. - A
let <pattern> = <expr>clause evaluates<expr>exactly once: - if the pattern matches, its binders are introduced and evaluation continues,
- otherwise the entire condition is
false. - A
let mut <pattern> = <expr>clause introduces mutable binders for the remaining clauses and thethenblock. - A
let move <pattern> = <expr>clause consumes the clause scrutinee for ownership-tracked values. The moved source binding is unavailable in later clauses, thethenblock, and theelseblock. - A non-
letclause must have typebool;falseshort-circuits. - Binders introduced by
letclauses are in scope for: - subsequent clauses in the chain, and
- the
thenblock. They are not in scope in theelseblock, and they do not escape theif.
Parsing note (Supported forms):
&&at the top level is parsed as a clause separator. Use parentheses if a clause needs its own&&/||/??expression at the top level.
Example (else let shorthand):
fn main () -> int {
let a: int? = None;
let b: int? = Some(3);
if let Some(v) = a {
return v;
} else let Some(v) = b {
return v;
} else {
return 0;
}
}
Supported patterns in the Supported forms (same as match expressions; see
flow match):
- optionals:
None,Some(name),Some(_) - recoverable results:
Ok(name),Err(name)(and_binders) - enums:
Variant(...)/E::Variant(...)/ qualified variants - type unions:
name: Type/_: Type
Example (optional):
fn main () -> int {
let maybe: int? = Some(7);
if let Some(v) = maybe {
return v;
}
return 0;
}
Example (recoverable Result):
import std::result;
fn main () -> int {
let r: std::result::Result(int, string) = Ok(42);
if let Ok(v) = r {
return v;
}
return 0;
}
Notes:
<condition>is an expression; parentheses are optional because the normal expression grammar already includes parenthesized expressions.- Bodies are blocks.
elsemay be followed by either: - a block (
else { ... }), or - another
if(else if ... { ... }) to form an “else-if” chain.
Surface Syntax (Expression Form)#
Silk also supports if / else as an expression form that yields a value:
let v: int = if cond { 123 } else { 456 };
Notes:
-
ifexpressions require anelsebranch so the expression yields a value on all paths. -
The
else if ...chain form is supported in expression position:let v: int = if a { 1 } else if b { 2 } else { 3 }; -
Restriction: the
{ ... }bodies ofifexpressions contain a single expression (not a full statement block).
Semantics#
- The condition expression is evaluated exactly once.
- If the condition is
true, theifblock executes and theelseblock (if present) does not execute. - If the condition is
false, theelseblock executes if present; otherwise theifstatement does nothing.
Blocks create scopes:
- Declarations inside the
ifbody are not visible outside that body. - Declarations inside the
elsebody are not visible outside that body.
Type Checking Rules#
- The condition must have type
bool. If it does not, the checker reports a type mismatch (diagnostics,E2001).
For if expressions:
- The
thenandelsebranches must produce compatible value types. - The expression’s result type is the shared branch type (or the expected type when the expression is type-directed).
else if Chains#
The language supports chained conditions (“else-if chains”). The compiler
parses else if as sugar for nesting an if inside the else block:
fn main () -> int {
let x: int = 1;
if x == 0 {
return 0;
} else {
if x == 1 {
return 1;
} else {
return 2;
}
}
}
The equivalent direct surface form is:
fn main () -> int {
let x: int = 1;
if x == 0 {
return 0;
} else if x == 1 {
return 1;
} else {
return 2;
}
}
Examples#
Minimal if / else#
fn main () -> int {
if true {
return 0;
} else {
return 1;
}
}
Boolean expressions in conditions#
fn main () -> int {
let x: int = 1;
let y: int = 2;
if x < y && y < 10 {
return 3;
} else {
return 4;
}
}
Control flow inside branches#
fn main () -> int {
let x: int = 1;
let y: int = 2;
if x < y {
while false {
continue;
}
return 3;
} else {
return 4;
}
}
Notes#
Supported forms:
if <expr> { ... }andif <expr> { ... } else { ... }statement forms.if let <pattern> = <expr> { ... }statement form:else if let/else letchains, and&&let-chains in theif letcondition.- Boolean type-checking for conditions.
ifexpressions of the formif <cond> { <expr> } else { <expr> }.
Limitations:
- General block expressions (
{ stmt* <expr> }) outside the specificifexpression form.
examples:
Source repository · Edit this page · View Markdown