Skip to content

Pattern Matching

XIOM supports structural pattern matching on enums, tuples, and literals via match and while let, plus the is test for variant checks without binding.


match Expression

The language requires a match to be exhaustive -- every variant must be covered. The current compiler does not reject every non-exhaustive match yet, so write the missing arms; a clean build is not proof of exhaustiveness.

match value {
  Some(v) => io.println(v),
  None => io.println("nothing"),
}

Multi-line Arms with Braces

When an arm requires multiple statements, use braces. Block arms need no separator:

match result {
  Ok(data) => {
    process(data);
    return data.len();
  }
  Err(e) => {
    io.println("error: " + e.message);
    return 0;
  }
}

Match on Integers

fn grade(score: Int) -> Str {
  match score {
    0 | 1 | 2 => { return "F"; }
    3 | 4 => { return "D"; }
    5 | 6 => { return "C"; }
    7 | 8 => { return "B"; }
    9 | 10 => { return "A"; }
    _ => { return "invalid"; }
  }
}

Match on Strings

fn command(cmd: Str) {
  match cmd {
    "start" => { io.println("starting..."); }
    "stop" => { io.println("stopping..."); }
    "restart" => { io.println("restarting..."); }
    _ => { io.println("unknown command"); }
  }
}

Pattern Types

Literal Patterns

Match exact values: integers, strings, characters, booleans.

match x {
  0 => "zero",
  1 => "one",
  _ => "other",
}

Enum Variant Patterns

Match enum constructors, optionally binding inner data.

match option {
  Some(v) => v,        // binds `v` to the inner value
  None => default,
}

Struct Patterns

Destructure structs by field:

match point {
  Point{x: 0, y: 0} => "origin",
  Point{x, y} => {
    // x and y are bound to the fields
    "somewhere";
  }
}

OR Patterns

Match any of several alternatives with |:

match code {
  200 | 201 | 204 => "success",
  400 | 404 => "client error",
  500 | 502 | 503 => "server error",
  _ => "unknown",
}

Wildcard Pattern _

Matches anything, discarding the value:

match result {
  Ok(v) => v,
  _ => default,
}

Binding Pattern is

Test if a value matches a pattern without destructuring:

if value is Some {
  io.println("value is Some");
}

Conditional Handling Without if let

if let is not implemented by the current compiler. Two supported forms cover the same cases.

Test the variant without binding:

if value is Some {
  io.println("value is Some");
}

Bind with match:

match find_user(id) {
  Some(user) => io.println("Found: " + user.name),
  None => io.println("User not found"),
}

Nested patterns work the same way in match:

match parse_and_lookup() {
  Ok(Some(data)) => process(data),
  _ => {},
}

while let -- Looping Destructuring

Continues looping while the pattern matches:

var iter = make_iterator();
while let Some(item) = iter.next() {
  process(item);
}

Example with a channel:

while let Ok(msg) = channel.try_recv() {
  handle(msg);
}

? Operator

The ? operator is syntactic sugar for match with early return on Err or None:

// These are equivalent:
let file = io.read_file(path)?;

// Expands to:
let file = match io.read_file(path) {
  Ok(f) => f,
  Err(e) => return Err(e.into()),
};

Works with both Result and Option:

fn lookup(key: Str) -> Option[Int] {
  let val = cache.get(&key)?;  // returns None if miss
  return Some(val);
}

Exhaustiveness

The language specifies that match expressions cover all possible cases. If a variant is missing, that is a defect and reported as a warning (W000) where the checker detects it; the current compiler does not reject every non-exhaustive match, so do not rely on the build to fail:

// ERROR: missing Pattern Green
match color {
  Color.Red => { ... }
  Color.Blue => { ... }
  // Color.Green is not covered!
}

Use _ as a catch-all to satisfy exhaustiveness:

match color {
  Color.Red => { ... }
  _ => { ... }          // catches remaining variants
}

Guards (Planned)

Pattern guards (match value { Some(v) if v > 0 => ... }) are not yet implemented. Use if inside the match arm body as a workaround:

match value {
  Some(v) => {
    if v > 0 {
      // guarded logic here
    }
  }
  None => {}
}

See Also