Generic Functions

Another use of comptime is generic functions, also known as polymorphic functions.

As an example, let’s define a clamp function that limits its values to be within a certain range:

const std = @import("std");
const print = std.debug.print;

fn clamp(comptime T: type, val: T, min: T, max: T) T {
    if (val < min) return min;
    if (val > max) return max;
    return val;
}

pub fn main() void {
    print("{}\n", .{clamp(f64, 1.2, 0.0, 1.0)});
    print("{}\n", .{clamp(u64, 120, 0, 100)});
}
$ zig run generic.zig 
1
100

What if we want clamp to work for arrays as well?

const std = @import("std");
const print = std.debug.print;

// Let’s start with a function just for arrays.
fn clampArray(T: type, val: T, min: T, max: T) T {
    // E is the type of the array elements.
    const E = @TypeOf(val[0]);
    var result: T = undefined;
    for (&result, 0..) |*p, i|
        p.* = clamp(E, val[i], min[i], max[i]);
    return result;
}

fn clamp(T: type, val: T, min: T, max: T) T {
    // Special case if T is an array type:
    if (@typeInfo(T) == .array)
        return clampArray(T, val, min, max);
    if (val < min) return min;
    if (val > max) return max;
    return val;
}

pub fn main() void {
    print("{}\n", .{clamp(f64, 1.2, 0.0, 1.0)});
    print("{}\n", .{clamp(u64, 120, 0, 100)});

    var a: [3]f64 = .{ -0.1, 0.4, 1.2 };
    a = clamp([3]f64, a, @splat(0.0), @splat(1.0));
    print("{} {} {}\n", .{ a[0], a[1], a[2] });
}
$ zig run generic-2.zig 
1
100
0 0.4 1

Next example: Generic Types.