Skip to content

`if` Expressions

In Zig, if is an expression. It can produce a value.

if Expressions

In Zig, if is an expression. It can produce a value.

const max = if (a > b) a else b;

This is common Zig style. The if expression chooses one of two values.

The condition must be a boolean value. Zig does not convert integers to booleans automatically.

This is correct:

if (x != 0) {
    // ...
}

This is wrong:

if (x) {
    // ...
}

If x is an integer, the compiler rejects it.

The basic form is:

if (condition) expression1 else expression2

Both branches must produce compatible types.

const value = if (true) 1 else 2;

This is valid because both branches are integers.

This is not:

const value = if (true) 1 else "hello";

The branches have different types.

Braces are optional when a branch contains a single expression.

const sign = if (n < 0) -1 else 1;

Longer branches usually use blocks.

const kind = if (n == 0) {
    "zero"
} else if (n < 0) {
    "negative"
} else {
    "positive"
};

The final semicolon belongs to the declaration, not the if.

Because if is an expression, it fits naturally into assignments and return statements.

fn abs(x: i32) i32 {
    return if (x < 0) -x else x;
}

The whole if expression becomes the return value.

An if branch may contain blocks.

const result = if (n > 100) blk: {
    const half = n / 2;
    break :blk half;
} else blk: {
    const doubled = n * 2;
    break :blk doubled;
};

This combines block expressions with if.

Conditions are evaluated from left to right.

if (a and b) {
    // ...
}

and stops early if the left side is false.

if (a or b) {
    // ...
}

or stops early if the left side is true.

These are short-circuit operators.

Optional values work naturally with if.

const maybe_name: ?[]const u8 = "zig";

if (maybe_name) |name| {
    std.debug.print("{s}\n", .{name});
}

If the optional contains a value, the value is captured in name.

Error unions also work with if.

const result: anyerror!i32 = 42;

if (result) |value| {
    std.debug.print("{d}\n", .{value});
} else |err| {
    std.debug.print("error: {}\n", .{err});
}

The success value is captured in the first branch. The error value is captured in the else branch.

This style appears often in Zig code because error handling is explicit.

An if expression may also produce void.

if (debug_mode) {
    std.debug.print("debug\n", .{});
}

Here there is no else branch because the result is not used.

The type of the whole expression is void.

Nested if expressions are common, but deep nesting is usually avoided.

if (x < 0) {
    // ...
} else if (x == 0) {
    // ...
} else {
    // ...
}

This is clearer than many separate if statements.

Exercise 3-5. Write an if expression that returns the smaller of two integers.

Exercise 3-6. Rewrite an ordinary if statement as an expression.

Exercise 3-7. Use if with an optional value.

Exercise 3-8. Write a function that returns "even" or "odd" using an if expression.