Generics
25.1 Generic Functions
fn max(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
_ = max(i32, 3, 7);
_ = max(f64, 1.5, 0.5);
fn sum(values: anytype) @TypeOf(values[0]) { // duck typing with anytype
var total: @TypeOf(values[0]) = 0;
for (values) |v| total += v;
return total;
}
_ = sum([_]u32{ 1, 2, 3 });25.2 Generic Types (Functions Returning Types)
Generic types are memoized: Stack(i32) called twice returns the same type.
fn Stack(comptime T: type) type {
return struct {
items: std.ArrayList(T) = .empty,
const Self = @This();
pub fn push(self: *Self, allocator: std.mem.Allocator, x: T) !void {
try self.items.append(allocator, x);
}
pub fn pop(self: *Self) ?T {
return self.items.pop();
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
self.items.deinit(allocator);
}
};
}
var s: Stack(i32) = .{};
defer s.deinit(allocator);
try s.push(allocator, 10);
_ = s.pop();
fn Pair(comptime A: type, comptime B: type) type {
return struct { first: A, second: B };
}
const p = Pair(u8, []const u8){ .first = 1, .second = "x" };
_ = p;25.3 Constraining Generics
There are no traits or interfaces in the type system. Constraints are expressed as comptime checks (@typeInfo, @hasDecl, @hasField, std.meta.hasFn) with good error messages.
fn average(comptime T: type, xs: []const T) T {
comptime switch (@typeInfo(T)) {
.float => {},
else => @compileError("average requires a float type"),
};
var total: T = 0;
for (xs) |x| total += x;
return total / @as(T, @floatFromInt(xs.len));
}
fn serialize(writer: *std.Io.Writer, value: anytype) !void {
const T = @TypeOf(value);
if (!@hasDecl(T, "serialize")) @compileError(@typeName(T) ++ " has no serialize method");
try value.serialize(writer);
}