Pattern Matching

Pattern matching in Aria is exhaustive — the compiler rejects incomplete matches. Patterns appear in match expressions, for loops, variable bindings, and catch blocks.

Match Expressions

fn describe(s: Shape) -> str = match s {
    Circle{radius} => "circle with radius {radius}"
    Rect{w, h} => "{w}x{h} rectangle"
    Point => "a point"
}

If you add a new variant to Shape, every match on it becomes a compile error (E0400) until updated. This is critical for AI-generated code — the compiler catches incomplete handling automatically. Exhaustiveness checking covers sum types, booleans, Option, and Result.

Or-Patterns

Match multiple patterns in a single arm with |:

match direction {
    North | South => "vertical"
    East | West => "horizontal"
}

match statusCode {
    200 | 201 | 204 => "success"
    400 | 422 => "client error"
    500 | 502 | 503 => "server error"
    code => "other: {code}"
}

Named Patterns with @

Bind a name to a matched value while also matching its structure:

match shape {
    s @ Circle{radius} if radius > 10.0 => {
        println("large circle: {s}")
    }
    _ => println("other shape")
}

Guards

msg := match temperature {
    t if t < 0 => "freezing"
    t if t < 20 => "cold"
    t if t < 30 => "comfortable"
    _ => "hot"
}

Nested Patterns

match response {
    Ok(Some(user)) => greet(user)
    Ok(None) => println("no user found")
    Err(e) => println("error: {e}")
}

Destructuring in Bindings

// Tuple destructuring
(lo, hi) := minMax(list)

// Struct destructuring
Point{x, y} := getOrigin()

// In for loops
for (index, value) in items.enumerate() {
    println("{index}: {value}")
}

for (key, value) in config {
    println("{key} = {value}")
}

Option Patterns

match findUser(id) {
    Some(user) => process(user)
    None => println("not found")
}

// Or use optional chaining
name := user?.profile?.displayName ?? "Anonymous"

Array & Slice Patterns

Destructure arrays and match on their structure:

match items {
    [] => println("empty")
    [only] => println("single: {only}")
    [first, ..rest] => {
        println("first: {first}, remaining: {rest.len()}")
    }
}

Refutable Bindings

Use pattern matching in bindings with an else fallback for when the pattern doesn't match:

Some(user) := findUser(id) else {
    println("user not found")
    return
}
// user is now bound and available

Float Patterns

Match on floating-point literals:

match value {
    0.0 => "zero"
    1.0 => "one"
    x => "{x}"
}

Wildcard Pattern

match command {
    "quit" => exit(0)
    "help" => showHelp()
    cmd => println("unknown command: {cmd}")
}

// Discard with _
match result {
    Ok(_) => println("success")
    Err(e) => println("failed: {e}")
}

Next Steps