Skip to content

Optionals

When a value can simply not exist — and that is not a failure — use the optional type T?. The type accepts the value nil (absent) or a value of type T (present). There is no Option.Some / Option.None in Zolo: presence is represented directly by the value.

T?, nil, ?? (coalescing), ?. (safe chaining), if let as a statement and as an expression.

05-optionals.zolo
Playground
// Feature: Optionals — `T?`, `nil`, `??`, `?.`, `if let`
// Syntax: type `T?`, value `nil` or `T`, operators `??` and `?.`
// When to use: missing value that is NOT an error (e.g., lookup miss).

// -- Optional type ---------------------------------------------
let name: str? = nil
let age: int? = 42
print(name)  // nil
print(age)  // 42

// -- ?? — null coalescing (default) ---------------------------
let display_name = name ?? "Anonymous"
let display_age = age ?? 0
print("{display_name}, {display_age}")

// expected: Anonymous, 42

// -- ?. — optional chaining (safe access) ---------------------
struct Address {
  city: str,
  zip: str,
}

struct User {
  name: str,
  address: Address,
}

let u = User { name: "Alice", address: Address { city: "Porto", zip: "01000" } }
let city = u?.address?.city ?? "Unknown"
print(city)

// expected: Porto

// -- if let — unwraps if non-nil ------------------------------
let v: int? = 42
if let x = v {
  print("got {x}")
} else {
  print("empty")
}

// expected: got 42

let none: int? = nil
if let x = none {
  print("got {x}")
} else {
  print("empty")
}

// expected: empty

// -- if let as expression -------------------------------------
let doubled = if let x = v { x * 2 } else { 0 }
print(doubled)  // 84

// -- Function returning an optional ---------------------------
fn find(id: int) -> str? {
  if id == 1 { return "Alice" }
  if id == 2 { return "Bob" }
  return nil
}

print(find(1) ?? "not found")  // Alice
print(find(3) ?? "not found")  // not found

// -- Chaining ?? with multiple defaults -----------------------
let a: str? = nil
let b: str? = nil
let c: str? = "value C"
print(a ?? b ?? c ?? "fallback")  // value C

The three main operators:

  • ?? returns the right-hand side when the left-hand side is nil — chainable (a ?? b ?? c).
  • ?. accesses a field or calls a method; if the receiver is nil, the entire result becomes nil instead of panicking.
  • if let x = optional { ... } unwraps the value; the else block covers the nil case.

Use T? + ?? for expected absences (a lookup that may find nothing). Use Result<T, E> when the absence carries error information.

Challenge

Add a fourth call find(4) ?? "not found" to the example. Then create a function find_upper(id: int) -> str? that uses ?. to call .to_upper() on the result of find — what happens for id == 99?

enespt-br