# `if` Expressions

### `if` Expressions

Programs need to make choices.

A program may need to ask:

Should I print this message?

Should I open this file?

Should I stop because something went wrong?

Should I use this value or another value?

In Zig, the simplest way to make a choice is with `if`.

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

pub fn main() void {
    const age = 20;

    if (age >= 18) {
        std.debug.print("adult\n", .{});
    }
}
```

This program prints:

```text
adult
```

The condition is:

```zig
age >= 18
```

This condition has type `bool`. A `bool` value is either `true` or `false`.

If the condition is `true`, Zig runs the code inside the braces.

If the condition is `false`, Zig skips it.

#### The Basic Shape

The basic shape of `if` is:

```zig
if (condition) {
    // code runs when condition is true
}
```

The condition must be inside parentheses.

The body must be a block. A block begins with `{` and ends with `}`.

Example:

```zig
const is_debug = true;

if (is_debug) {
    std.debug.print("debug mode\n", .{});
}
```

Here, `is_debug` is already a boolean value, so it can be used directly as the condition.

#### `if` with `else`

Most choices have two paths.

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

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

    if (score >= 60) {
        std.debug.print("pass\n", .{});
    } else {
        std.debug.print("fail\n", .{});
    }
}
```

This program prints:

```text
pass
```

The `else` block runs only when the `if` condition is false.

The shape is:

```zig
if (condition) {
    // true path
} else {
    // false path
}
```

Only one branch runs.

If the condition is true, Zig runs the first block and skips the `else` block.

If the condition is false, Zig skips the first block and runs the `else` block.

#### `if` with `else if`

Sometimes there are more than two cases.

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

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

    if (score >= 90) {
        std.debug.print("grade A\n", .{});
    } else if (score >= 80) {
        std.debug.print("grade B\n", .{});
    } else if (score >= 70) {
        std.debug.print("grade C\n", .{});
    } else {
        std.debug.print("grade D or below\n", .{});
    }
}
```

This program prints:

```text
grade B
```

Zig checks the conditions from top to bottom.

First:

```zig
score >= 90
```

That is false.

Then:

```zig
score >= 80
```

That is true, so Zig runs that block and stops checking the rest.

The later branches are skipped.

Order matters. This version is correct:

```zig
if (score >= 90) {
    std.debug.print("grade A\n", .{});
} else if (score >= 80) {
    std.debug.print("grade B\n", .{});
}
```

This version is wrong for grading:

```zig
if (score >= 80) {
    std.debug.print("grade B\n", .{});
} else if (score >= 90) {
    std.debug.print("grade A\n", .{});
}
```

A score of `95` is also greater than `80`, so the first branch runs. The `score >= 90` branch is never reached.

#### Conditions Must Be Boolean

Zig is strict about conditions.

In many languages, numbers can act like booleans. For example, `0` may mean false and `1` may mean true.

Zig does not do that.

This is invalid:

```zig
const n = 1;

if (n) {
    // invalid Zig
}
```

`n` is an integer, not a boolean.

You must write the condition directly:

```zig
const n = 1;

if (n != 0) {
    std.debug.print("n is not zero\n", .{});
}
```

This is one of Zig’s important habits: make the test explicit.

Instead of asking Zig to guess what you mean, say exactly what you mean.

#### Common Comparison Operators

You often build `if` conditions with comparison operators.

| Operator | Meaning |
|---|---|
| `==` | equal to |
| `!=` | not equal to |
| `<` | less than |
| `<=` | less than or equal to |
| `>` | greater than |
| `>=` | greater than or equal to |

Example:

```zig
const x = 10;

if (x == 10) {
    std.debug.print("x is 10\n", .{});
}

if (x != 0) {
    std.debug.print("x is not zero\n", .{});
}

if (x > 5) {
    std.debug.print("x is greater than 5\n", .{});
}
```

Each comparison produces a `bool`.

#### Combining Conditions

You can combine boolean conditions.

Use `and` when both conditions must be true.

```zig
const age = 25;
const has_ticket = true;

if (age >= 18 and has_ticket) {
    std.debug.print("you may enter\n", .{});
}
```

Use `or` when at least one condition must be true.

```zig
const is_admin = false;
const is_owner = true;

if (is_admin or is_owner) {
    std.debug.print("access allowed\n", .{});
}
```

Use `!` to reverse a boolean.

```zig
const logged_in = false;

if (!logged_in) {
    std.debug.print("please log in\n", .{});
}
```

Read `!logged_in` as “not logged in.”

#### `if` Is an Expression

In Zig, `if` can produce a value.

That means you can write:

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

pub fn main() void {
    const age = 20;

    const message = if (age >= 18) "adult" else "minor";

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

This program prints:

```text
adult
```

The important line is:

```zig
const message = if (age >= 18) "adult" else "minor";
```

If the condition is true, the whole `if` expression becomes:

```zig
"adult"
```

If the condition is false, it becomes:

```zig
"minor"
```

Then that value is stored in `message`.

This is useful when you want to choose a value, not run a large block of code.

#### Both Branches Must Match

When `if` returns a value, both branches must produce compatible types.

This is valid:

```zig
const n = 10;
const label = if (n >= 0) "positive" else "negative";
```

Both branches produce string literals.

This is invalid:

```zig
const n = 10;
const value = if (n >= 0) 123 else "negative";
```

One branch gives an integer. The other branch gives a string. Zig will reject this because `value` needs one type.

Zig wants the type of each expression to be clear.

#### Blocks Can Return Values

An `if` expression can use blocks too.

When a block should produce a value, use `break` with the block value.

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

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

    const result = if (score >= 60) blk: {
        std.debug.print("checking pass branch\n", .{});
        break :blk "pass";
    } else blk: {
        std.debug.print("checking fail branch\n", .{});
        break :blk "fail";
    };

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

This program prints:

```text
checking pass branch
result: pass
```

The label `blk` names the block. The line:

```zig
break :blk "pass";
```

means: leave this block and use `"pass"` as the block’s value.

For beginners, this syntax may look unusual. You do not need it for simple `if` statements. But it matters later because Zig treats blocks as expressions.

#### `if` with Optionals

Zig has optional values. An optional value may contain something, or it may contain nothing.

The “nothing” value is `null`.

Example:

```zig
const maybe_number: ?u32 = 42;
```

The type `?u32` means: either a `u32`, or `null`.

You can use `if` to unwrap an optional:

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

pub fn main() void {
    const maybe_number: ?u32 = 42;

    if (maybe_number) |number| {
        std.debug.print("number: {}\n", .{number});
    } else {
        std.debug.print("no number\n", .{});
    }
}
```

This prints:

```text
number: 42
```

This part:

```zig
if (maybe_number) |number| {
```

means: if `maybe_number` contains a value, take the value out and call it `number`.

Inside the block, `number` is a normal `u32`.

If `maybe_number` is `null`, the `else` block runs.

```zig
const maybe_number: ?u32 = null;

if (maybe_number) |number| {
    std.debug.print("number: {}\n", .{number});
} else {
    std.debug.print("no number\n", .{});
}
```

This prints:

```text
no number
```

This is one of the most common uses of `if` in Zig.

#### `if` with Error Unions

Zig also uses `if` with error unions.

An error union is a value that may be either a successful value or an error.

For example:

```zig
fn parseNumber() !u32 {
    return 123;
}
```

The return type `!u32` means: this function returns either a `u32` or an error.

You can handle that with `if`:

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

fn parseNumber() !u32 {
    return 123;
}

pub fn main() void {
    if (parseNumber()) |number| {
        std.debug.print("number: {}\n", .{number});
    } else |err| {
        std.debug.print("error: {}\n", .{err});
    }
}
```

If `parseNumber()` succeeds, the success value is assigned to `number`.

If it fails, the error is assigned to `err`.

This style makes success and failure visible in the code.

#### When to Use `if`

Use `if` when your program needs to choose between paths.

Use a plain `if` when there is only one special case.

```zig
if (count == 0) {
    std.debug.print("empty\n", .{});
}
```

Use `if` and `else` when there are two paths.

```zig
if (count == 0) {
    std.debug.print("empty\n", .{});
} else {
    std.debug.print("not empty\n", .{});
}
```

Use `else if` when there are several ordered tests.

```zig
if (score >= 90) {
    std.debug.print("A\n", .{});
} else if (score >= 80) {
    std.debug.print("B\n", .{});
} else {
    std.debug.print("lower\n", .{});
}
```

Use `switch` when you are comparing one value against many exact cases. We will cover `switch` in the next section.

#### The Main Idea

`if` lets a Zig program make a choice.

The condition must be a boolean. The true branch runs when the condition is true. The `else` branch runs when the condition is false.

Zig also lets `if` produce a value, unwrap optionals, and handle error unions.

The beginner rule is simple: write the condition clearly, keep each branch small, and make every choice visible in the code.

