> Pattern matching is one of Jitzu's most powerful features. Combined with union types and the built-in Result/Option system, it lets you handle different cases elegantly and safely.

# Pattern Matching

Pattern matching is one of Jitzu's most powerful features. Combined with union types and the
built-in Result/Option system, it lets you handle different cases elegantly and safely.

## Union Types

Union types allow a value to be one of several variants, providing type-safe alternatives to traditional enums.

### Defining Union Types

```jitzu
// Basic union type
union Pet {
    Fish,
    Cat(String),           // Cat with name
    Dog(String, Int),      // Dog with name and age
    Bird(String, Bool),    // Bird with name and can_talk
    None,
}

// Union for error handling
union FileResult {
    Success(String),
    NotFound,
    PermissionDenied,
    InvalidFormat(String),
}
```

### Creating Union Instances

```jitzu
// Creating union instances
let my_pet = Pet.Cat("Whiskers")
let family_dog = Pet.Dog("Rex", 5)
let goldfish = Pet.Fish
let no_pet = Pet.None

// Result instances
let success = FileResult.Success("File content here")
let error = FileResult.InvalidFormat("Not a valid JSON file")
```

## Match Expressions

Match expressions provide exhaustive pattern matching over union types.

### Basic Pattern Matching

```jitzu
// Basic match expression
let pet = Pet.Cat("Whiskers")

match pet {
    Pet.Fish => print("Fish don't need names"),
    Pet.Cat(name) => print(\`Hello cat {name}\`),
    Pet.Dog(name, age) => print(\`Dog {name} is {age} years old\`),
    Pet.Bird(name, can_talk) => {
        if can_talk {
            print(\`{name} the talking bird\`)
        } else {
            print(\`{name} the quiet bird\`)
        }
    },
    Pet.None => print("No pets"),
}

// Match expressions return values
let pet_description = match pet {
    Pet.Fish => "A silent swimmer",
    Pet.Cat(name) => \`A cat named {name}\`,
    Pet.Dog(name, age) => \`A {age}-year-old dog named {name}\`,
    Pet.Bird(name, _) => \`A bird named {name}\`,
    Pet.None => "No pet"
}
```

### Wildcard Patterns

Use `_` to ignore values you don't need:

```jitzu
match pet {
    Pet.Dog(name, _) => print(\`Dog: {name}\`),  // Ignore age
    _ => print("Not a dog"),                      // Match anything else
}
```

## Result Types

Result types are a built-in union for handling operations that can succeed or fail.

### Working with Results

```jitzu
// Function returning Result
fun divide(a: Double, b: Double): Result<Double, String> {
    if b == 0.0 {
        Err("Division by zero")
    } else {
        Ok(a / b)
    }
}

// Pattern matching Results
let result = divide(10.0, 2.0)

match result {
    Ok(value) => print(\`Result: {value}\`),
    Err(error) => print(\`Error: {error}\`)
}
```

### The Try Operator

Use `try` to propagate errors without nested match statements:

```jitzu
fun safe_sqrt(x: Double): Result<Double, String> {
    if x < 0.0 {
        Err("Cannot take square root of negative number")
    } else {
        Ok(x)
    }
}

// Using try for early return on errors
fun complex_calculation(a: Double, b: Double, c: Double): Result<Double, String> {
    let step1 = try divide(a, b)        // Returns Err early if division fails
    let step2 = try safe_sqrt(step1)    // Returns Err early if sqrt fails
    let step3 = try divide(step2, c)    // Returns Err early if division fails
    Ok(step3)
}

// Without try (more verbose)
fun complex_calculation_verbose(a: Double, b: Double, c: Double): Result<Double, String> {
    match divide(a, b) {
        Ok(step1) => {
            match safe_sqrt(step1) {
                Ok(step2) => divide(step2, c),
                Err(e) => Err(e)
            }
        },
        Err(e) => Err(e)
    }
}
```

## Option Types

Option types handle nullable values safely.

### Option Patterns

```jitzu
// Function returning Option
fun find_user(users: User[], id: Int): Option<User> {
    for user in users {
        if user.id == id {
            return Some(user)
        }
    }
    None
}

// Pattern matching Options
match find_user(users, 1) {
    Some(user) => print(\`Found user: {user.name}\`),
    None => print("User not found")
}
```

## Best Practices

- **Cover all cases** - Match expressions should be exhaustive
- **Use wildcard patterns** - Use `_` for cases you don't care about
- **Prefer Result/Option** - Use them instead of null checks for safer code
- **Use try** - The try operator keeps error propagation clean and readable

## Common Errors

### Mismatched branch types

```jitzu
let label = match status {
    Ok(v) => \`Success: {v}\`,
    Err(e) => 42,  // returns Int, not String
}
// Error: Couldn't resolve match to a single return type

// Fix: all branches must return the same type
let label = match status {
    Ok(v) => \`Success: {v}\`,
    Err(e) => \`Error: {e}\`,
}
```

### Destructuring a type without a constructor

```jitzu
type Config { pub path: String }

match config {
    Config(p) => print(p),
}
// Error: Type Config does not have a constructor

// Fix: use field access instead of destructuring
print(config.path)
```

### Ambiguous type name

```jitzu
// When two packages export the same type name
let s = JsonSerializer {}
// Error: Type 'JsonSerializer' is ambiguous. Did you mean:
//   - System.Text.Json.JsonSerializer
//   - Newtonsoft.Json.JsonSerializer

// Fix: use the fully qualified name
let s = System.Text.Json.JsonSerializer {}
```

Pattern matching makes Jitzu code more expressive and safer by ensuring all cases are handled.
It's particularly powerful when combined with union types and the Result/Option system. Next, explore [.NET Interop](/docs/language/dotnet-interop) to learn how to use NuGet packages and call .NET methods.
