Skip to content

if / else

if chooses a path based on a boolean condition. As a statement, it runs a block:

Classic if / else if / else.

01-if-statement.zolo
Playground
// Feature: If as a statement
// Syntax: `if cond { ... } else if cond { ... } else { ... }`
// When to use: branching execution with side effects
// (logs, mutation, early-return). When you just want to run code
// and don't need a value back, use the statement form.

let x = 15

// Simple form: just if.
if x > 10 {
  print("x is greater than 10")
}

// if / else.
if x % 2 == 0 {
  print("even")
} else {
  print("odd")
}

// if / else if / else chain — evaluated top-down,
// stops at the first truthy branch.
if x > 100 {
  print("huge")
} else if x > 50 {
  print("large")
} else if x > 10 {
  print("medium")
} else {
  print("small")
}
// expected: medium

But if is also an expression: each branch is a value, and the whole if "becomes" that value — no temporary variable needed.

The chosen branch is the value of the expression.

02-if-expression.zolo
Playground
// Feature: If as an expression (returns a value)
// Syntax: `let v = if cond { val_a } else { val_b }`
// When to use: replace the ternary; compute a value
// dependent on a condition in a single expression without
// needing an intermediate mutable variable.

// The last value of each block is the value of the expression.
let x = 10
let label = if x > 5 { "large" } else { "small" }
print(label)  // large

// Chain in expression form — works the same as the statement.
fn classify(n: int) -> str {
  return if n > 0 {
    "positive"
  } else if n < 0 {
    "negative"
  } else {
    "zero"
  }
}

print(classify(7))  // positive
print(classify(-3))  // negative
print(classify(0))  // zero

// Useful in arithmetic/format expressions.
fn abs(n: int) -> int {
  return if n < 0 { -n } else { n }
}

print(abs(-42))  // 42

// Works in interpolation too — `if` is just an expression.
let n = 3
let parity = if n % 2 == 0 { "even" } else { "odd" }
print("number {n} is {parity}")
// expected: number 3 is odd

When the condition is "does this value match this pattern?", use if-let:

Matches a pattern and binds the variables only inside the block.

03-if-let.zolo
Playground
// Feature: `if let` — pattern match in a conditional
// Syntax: `if let Pattern = expr { ... } else { ... }`
// When to use: extract a value from Option/enum when you only
// care about one case (shortcut for `match` with 2 arms).

enum Opt {
  Some(int),
  None,
}

let v1 = Opt.Some(42)
let v2 = Opt.None

// Successful match — `x` is available inside the block.
if let Opt::Some(x) = v1 {
  print("Got: {x}")  // Got: 42
} else {
  print("empty")
}

// Match fails — falls through to else.
if let Opt::Some(x) = v2 {
  print("Got: {x}")
} else {
  print("empty")  // empty
}

// `if let` also works for direct optional `T?`.
let maybe: int? = 7
if let v = maybe {
  print("double: {v * 2}")  // double: 14
}

// As an expression, returning a value.
let text = if let Opt::Some(n) = v1 { "n={n}" } else { "nothing" }
print(text)  // n=42

And when the failure path must exit early (return/break), use let-else:

Destructures on the happy path; diverges in the else.

04-let-else.zolo
Playground
// Feature: `let else` — destructuring with early-return
// Syntax: `let Pattern = expr else { return / panic / break ... }`
// When to use: you want to destructure (refutable) and diverge
// on failure. Keeps the "happy path" without the extra indentation
// of `if let`.

// Take first element or return -1.
fn first(arr: [int]) -> int {
  let [head, ..rest] = arr else {
    return -1
  }
  return head
}

print(first([10, 20, 30]))  // 10
print(first([]))  // -1

// Destructuring an enum in let-else.
enum Resp {
  Ok(int),
  Err(str),
}

fn double_or_zero(r: Resp) -> int {
  let Resp::Ok(n) = r else {
    return 0
  }
  return n * 2
}

print(double_or_zero(Resp.Ok(21)))  // 42
print(double_or_zero(Resp.Err("x")))  // 0

See also

enespt-br