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.