Skip to Content

If

if has four uses: conditional statement, conditional expression, conditional binding, and type-test entry point. The condition slot accepts only a bool; an integer or any other type is not allowed there.

if, elif, else

func Grade(score: int) -> string { if score >= 90 { return "excellent"; } elif score >= 75 { return "good"; } elif score >= 60 { return "pass"; } else { return "fail"; } } print(Grade(95), Grade(80), Grade(65), Grade(50));
excellent good pass fail
  • Parentheses around the condition are optional; if (x > 0) and if x > 0 are equivalent.
  • Use elif for multi-way branching. else if does not exist; the two-word form raises MS2016.
  • When the condition is true but the branch is empty, write the body as { }.

The if Expression

Used in a value position, if is an expression: both arms must have the same type, and else is mandatory:

val label = if 5 > 3 { "yes" } else { "no" }; print(label);
yes

The last expression in a body is the branch’s value, and a single-expression statement may drop the semicolon. An elif chain works as an expression too; the trailing else is still required:

val w = if n < 0 { "negative" } elif n == 0 { "zero" } else { "positive" };

if val and if var: Conditional Bindings

if val pattern = expression takes the expression apart by the pattern; on a successful match the block runs with the bindings visible inside it, and on failure control goes to else:

var maybe: int? = 42; if val n = maybe { print("got", n); } else { print("none"); } maybe = null; if val n = maybe { print("got", n); } else { print("none"); }
got 42 none
  • A val binding is read-only; a var binding can be reassigned inside the block.
if var m = 7 { m = 8; print("var binding reassigned", m); }
var binding reassigned 8
  • The pattern slot supports every form on the Pattern page, not just the null check.
  • The bound variables live only inside the binding block; the else block cannot see them, because the match already failed.

is Declaration Patterns

x is T p tests a type in the condition slot and binds p to the narrowed value:

val box: any = "some text"; if box is string t { print("length", t.Length); }
length 9

Inside the block, t is a string, so members are directly accessible. This form only accepts a bare condition or an && chain: the negated form !(x is T p) and a plain bool position (val b = x is T p;) are both rejected.

guard: An if with Only an else Block

An if with an else block and no then block is a guard: the else block must diverge, ending in return / fail / crash (inside loop bodies, break / continue also count). Getting past a guard means the condition held:

func Read(b: Box?) -> int { if (b != null) else { return -1; } return b.v; // b is treated as non-null here; null-check narrowing flows through the guard }

The condition slot can also be a val / var / const binding (the pattern-matching form, taking over the retired expect semantics): when the pattern matches, the bound variables are available after the guard; on failure, control goes to else:

enum Result { Ok(value: int), Bad, } error DemoNeedsOk(); func Demo(r: Result) -> int { if val Result.Ok(n) = r else { crash new DemoNeedsOk(); } return n * 2; }

Two disciplines for guards:

  • else must diverge: “execution reaches here = the condition held” is the basis for narrowing and binding visibility, not syntactic sugar;
  • the pattern must be refutable: a binding that can never fail, as in if val n = 42 else { ... }, raises MS4245 when given an else (drop the else, or switch to a variant/literal pattern).

The division of labor with if: a regular if says both paths are normal; a guard says this path is blocked, so leave. Use guards for precondition checks, argument validation, and unwrap assertions.

Notes

  • Null-check narrowing is not limited to if val: after a guard-style check such as if maybe == null else { return; }, maybe is treated as non-null in the code that follows.
  • Boxed scalars (int? and friends) are not narrowed by an == null test; use if val for null-check binding.
  • An if expression is less direct than match for multi-way classification; past three branches, switch to match.
Last updated on October 11, 2026