# `switch`

### `switch`

An `if` statement is good for general conditions:

```zig
if (age >= 18) {
    // ...
}
```

But sometimes you are not testing ranges or complex logic. You are matching one value against several exact cases.

For example:

- a menu choice
- a command name
- a keyboard key
- an enum value
- a token type in a parser

This is where `switch` is useful.

A `switch` compares one value against multiple possible patterns.

#### Your First `switch`

```zig
const std = @import("std");

pub fn main() void {
    const day = 3;

    switch (day) {
        1 => std.debug.print("Monday\n", .{}),
        2 => std.debug.print("Tuesday\n", .{}),
        3 => std.debug.print("Wednesday\n", .{}),
        else => std.debug.print("Unknown day\n", .{}),
    }
}
```

This program prints:

```text
Wednesday
```

The value being checked is:

```zig
day
```

Zig compares it against each case:

```zig
1 => ...
2 => ...
3 => ...
```

When Zig finds a matching case, it runs that branch.

If nothing matches, the `else` branch runs.

#### The Shape of a `switch`

The general shape is:

```zig
switch (value) {
    pattern1 => expression1,
    pattern2 => expression2,
    else => fallback_expression,
}
```

Each branch uses:

```zig
pattern => result
```

The arrow `=>` means “if this pattern matches, use this result.”

#### `switch` Is an Expression

Like `if`, `switch` can produce a value.

```zig
const std = @import("std");

pub fn main() void {
    const day = 2;

    const name = switch (day) {
        1 => "Monday",
        2 => "Tuesday",
        3 => "Wednesday",
        else => "Unknown",
    };

    std.debug.print("{s}\n", .{name});
}
```

This prints:

```text
Tuesday
```

The whole `switch` expression becomes one value.

If `day` is `2`, the result becomes:

```zig
"Tuesday"
```

That value is stored in `name`.

This style is very common in Zig.

#### `switch` Does Not Fall Through

In C, `switch` statements can “fall through” accidentally.

Example C code:

```c
switch (x) {
    case 1:
        printf("one\n");

    case 2:
        printf("two\n");
}
```

If `x` is `1`, this prints both:

```text
one
two
```

because execution falls into the next case unless you write `break`.

Zig does not work this way.

In Zig, each branch is separate and isolated.

```zig
switch (x) {
    1 => std.debug.print("one\n", .{}),
    2 => std.debug.print("two\n", .{}),
    else => {},
}
```

Only one branch runs.

This removes a very common source of bugs.

#### Multiple Patterns in One Branch

You can combine several patterns.

```zig
const std = @import("std");

pub fn main() void {
    const c = 'a';

    switch (c) {
        'a', 'e', 'i', 'o', 'u' => {
            std.debug.print("vowel\n", .{});
        },
        else => {
            std.debug.print("not vowel\n", .{});
        },
    }
}
```

This prints:

```text
vowel
```

The branch runs if any listed pattern matches.

#### Ranges in `switch`

You can also match ranges.

```zig
const std = @import("std");

pub fn main() void {
    const score = 85;

    switch (score) {
        90...100 => std.debug.print("grade A\n", .{}),
        80...89 => std.debug.print("grade B\n", .{}),
        70...79 => std.debug.print("grade C\n", .{}),
        else => std.debug.print("lower grade\n", .{}),
    }
}
```

This prints:

```text
grade B
```

The syntax:

```zig
80...89
```

means “all integers from 80 through 89 inclusive.”

Ranges make some `switch` statements much cleaner than long `if else if` chains.

#### Blocks Inside Branches

A branch can contain a block.

```zig
const std = @import("std");

pub fn main() void {
    const command = 1;

    switch (command) {
        1 => {
            std.debug.print("open file\n", .{});
            std.debug.print("loading data\n", .{});
        },
        2 => {
            std.debug.print("save file\n", .{});
        },
        else => {
            std.debug.print("unknown command\n", .{});
        },
    }
}
```

Use blocks when the branch needs multiple statements.

#### Matching Enums

`switch` becomes especially powerful with enums.

```zig
const std = @import("std");

const Direction = enum {
    north,
    south,
    east,
    west,
};

pub fn main() void {
    const dir = Direction.east;

    switch (dir) {
        .north => std.debug.print("up\n", .{}),
        .south => std.debug.print("down\n", .{}),
        .east => std.debug.print("right\n", .{}),
        .west => std.debug.print("left\n", .{}),
    }
}
```

This prints:

```text
right
```

Notice the enum values:

```zig
.north
.south
.east
.west
```

Inside the `switch`, Zig already knows the type is `Direction`, so the enum name can be omitted.

#### Exhaustive Checking

One of Zig’s best features is exhaustive checking.

If every enum case is handled, Zig knows the code is complete.

Example:

```zig
const Direction = enum {
    north,
    south,
};

fn move(dir: Direction) void {
    switch (dir) {
        .north => {},
        .south => {},
    }
}
```

This is complete.

But suppose you later add:

```zig
east
```

Now the compiler reports an error because the `switch` no longer handles every possibility.

This is extremely valuable in large programs. The compiler helps you update all affected code safely.

#### Using `else`

You can use `else` as a fallback branch.

```zig
switch (value) {
    1 => {},
    2 => {},
    else => {},
}
```

But when switching over enums, many Zig programmers prefer exhaustive handling without `else`.

Why?

Because if you add a new enum value later, the compiler forces you to update the `switch`.

Example:

```zig
const State = enum {
    idle,
    running,
    stopped,
};

switch (state) {
    .idle => {},
    .running => {},
    .stopped => {},
}
```

This is safer than:

```zig
switch (state) {
    .idle => {},
    else => {},
}
```

The second version silently hides future enum values inside `else`.

#### Capturing Values

`switch` can capture matched values.

```zig
const std = @import("std");

const Token = union(enum) {
    number: i32,
    word: []const u8,
};

pub fn main() void {
    const token = Token{ .number = 42 };

    switch (token) {
        .number => |n| {
            std.debug.print("number: {}\n", .{n});
        },
        .word => |w| {
            std.debug.print("word: {s}\n", .{w});
        },
    }
}
```

This prints:

```text
number: 42
```

This syntax:

```zig
.number => |n|
```

means:

- match the `.number` case
- extract the stored value
- name it `n`

This feature is very important when working with tagged unions.

#### `switch` and Types

All branches in a value-returning `switch` must produce compatible types.

This is valid:

```zig
const result = switch (x) {
    1 => "one",
    2 => "two",
    else => "other",
};
```

All branches return strings.

This is invalid:

```zig
const result = switch (x) {
    1 => 123,
    else => "hello",
};
```

One branch returns an integer. The other returns a string.

Zig requires a consistent result type.

#### `switch` vs `if`

Use `if` when:

- conditions are unrelated
- conditions involve comparisons
- conditions involve boolean logic

Example:

```zig
if (x > 0 and x < 100) {
    // ...
}
```

Use `switch` when:

- matching one value
- handling enums
- handling many exact cases
- matching ranges cleanly

Example:

```zig
switch (command) {
    .open => {},
    .save => {},
    .quit => {},
}
```

#### Empty Branches

Sometimes a branch intentionally does nothing.

```zig
switch (value) {
    0 => {},
    else => std.debug.print("not zero\n", .{}),
}
```

The empty block:

```zig
{}
```

means “do nothing.”

#### The Main Idea

A `switch` compares one value against multiple patterns.

It is cleaner and safer than long chains of `if else if` when many exact cases exist.

Zig’s `switch` has several important properties:

- no accidental fallthrough
- exhaustive enum checking
- ranges and multiple patterns
- value-returning expressions
- pattern matching with captured values

You will use `switch` constantly in real Zig programs, especially when working with enums, parsers, protocols, compilers, and state machines.

