Functions
8.1 Basics
Parameters are immutable (they're constants). To modify the caller's data, pass a pointer.
Primitive types are passed by value. The compiler may pass large structs by reference (that's an implementation detail, so don't rely on aliasing).
No default arguments and no overloading. Use option structs or comptime parameters.
fn add(a: i32, b: i32) i32 {
return a + b;
}
pub fn greet(name: []const u8) void {
std.debug.print("Hello, {s}\n", .{name});
}8.2 Pointers for Mutation
fn increment(x: *i32) void {
x.* += 1;
}
var n: i32 = 5;
increment(&n);8.3 Returning Multiple Values
fn divmod(a: u32, b: u32) struct { q: u32, r: u32 } {
return .{ .q = a / b, .r = a % b };
}
const res = divmod(17, 5);
std.debug.print("{d} {d}\n", .{ res.q, res.r });
fn minMax(xs: []const i32) struct { i32, i32 } { // tuple return type
var lo = xs[0];
var hi = xs[0];
for (xs) |x| { lo = @min(lo, x); hi = @max(hi, x); }
return .{ lo, hi };
}
const lo, const hi = minMax(&.{ 3, 1, 9 });8.4 Option Structs (Named and Default Parameters)
const ServerOptions = struct {
port: u16 = 8080,
host: []const u8 = "127.0.0.1",
max_conn: u32 = 100,
};
fn startServer(opts: ServerOptions) void {
std.debug.print("{s}:{d}\n", .{ opts.host, opts.port });
}
startServer(.{}); // all defaults
startServer(.{ .port = 9000 });8.5 Function Pointers and Function Types
No closures. Pass a context pointer and a function pointer instead (the "context + callback" pattern), or use a struct with a method.
const BinOp = *const fn (i32, i32) i32;
fn apply(op: BinOp, a: i32, b: i32) i32 { return op(a, b); }
fn mul(a: i32, b: i32) i32 { return a * b; }
_ = apply(mul, 3, 4);
_ = apply(&add, 3, 4);8.6 comptime and anytype Parameters (Preview of Module 24)
fn max(comptime T: type, a: T, b: T) T { return if (a > b) a else b; }
fn printAny(x: anytype) void { std.debug.print("{any}\n", .{x}); }8.7 Calling Conventions and Attributes
export fn c_callable(x: c_int) c_int { return x * 2; } // exported with the C ABI
fn cb(x: c_int) callconv(.c) c_int { return x; } // explicit C calling convention
inline fn fastPath() void {} // force inlining (semantic, not just a hint)
noinline fn coldPath() void {}
fn neverReturns() noreturn { @panic("fatal"); }
extern "c" fn printf(fmt: [*:0]const u8, ...) c_int; // extern declaration (variadic)8.8 Recursion
There's no tail-call guarantee, but @call(.always_tail, f, args) requests one. Watch stack depth (stack size is configurable per thread).
fn fib(n: u64) u64 {
return if (n < 2) n else fib(n - 1) + fib(n - 2);
}