⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Functions

const std = @import("std");
const expect = std.testing.expect;

fn addFive(x: u32) u32 {
    // Parameters are const. `x += 5` here would not compile.
    return x + 5;
}

fn fibonacci(n: u16) u16 {
    if (n == 0 or n == 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

test "calling a function" {
    const y = addFive(0);
    try expect(@TypeOf(y) == u32);
    try expect(y == 5);
}

test "recursion" {
    try expect(fibonacci(10) == 55);
}

test "values must be used" {
    // Zig has no unused-value warning because it is an error. `_ =` is the
    // explicit way to discard something.
    _ = addFive(1);
}

Parameters are constant

Function parameters are immutable. This does not compile:

fn addFive(x: u32) u32 {
    x += 5;      // error: cannot assign to constant
    return x;
}

If you want a mutable local, make one. The benefit is that a parameter always means what it meant at the call site, all the way down the function body.

Unused values are errors

Zig has no “unused variable” warning, because it is an error. Discarding is explicit:

_ = addFive(1);

This sounds pedantic until you realise it catches the case where you called a function for a result you then forgot to use.

Recursion and the stack

fibonacci above recurses happily, but Zig cannot always prove a bound on stack usage. For deep or input-driven recursion, an explicit stack or an iterative formulation is the safer choice: there is no automatic tail-call guarantee to lean on.