Interfaces and Polymorphism Patterns

Zig has no built-in interfaces, so it uses explicit patterns.

27.1 Compile-Time Polymorphism (anytype / Duck Typing)

fn drawAll(shapes: anytype) void {
    inline for (shapes) |s| s.draw();      // any type with a draw() method
}

27.2 Tagged Union Dispatch (Closed Set)

Fast (no indirection), exhaustive, and cache-friendly. Best when the set of types is known.

const Shape = union(enum) {
    circle: Circle,
    square: Square,
    pub fn area(self: Shape) f32 {
        return switch (self) {
            inline else => |s| s.area(),    // forwards to each variant's method
        };
    }
};

27.3 Runtime Interface: VTable / Fat Pointer (Open Set)

This is exactly how std.mem.Allocator and std.Io.Writer work.

const Writer = struct {
    ptr: *anyopaque,
    vtable: *const VTable,

    const VTable = struct {
        write: *const fn (ptr: *anyopaque, bytes: []const u8) anyerror!usize,
    };

    pub fn write(self: Writer, bytes: []const u8) !usize {
        return self.vtable.write(self.ptr, bytes);
    }
};

const ConsoleSink = struct {
    count: usize = 0,
    fn writeImpl(ptr: *anyopaque, bytes: []const u8) anyerror!usize {
        const self: *ConsoleSink = @ptrCast(@alignCast(ptr));
        self.count += bytes.len;
        std.debug.print("{s}", .{bytes});
        return bytes.len;
    }
    pub fn writer(self: *ConsoleSink) Writer {
        return .{ .ptr = self, .vtable = &.{ .write = writeImpl } };
    }
};

27.4 @fieldParentPtr Interfaces (Embedded Interface)

std.Io.Writer in 0.15 uses a similar "interface embedded in the implementation" design with the buffer inside the interface.

const Animal = struct {
    speakFn: *const fn (*Animal) void,
    pub fn speak(self: *Animal) void { self.speakFn(self); }
};
const Dog = struct {
    animal: Animal = .{ .speakFn = speak },
    name: []const u8,
    fn speak(a: *Animal) void {
        const self: *Dog = @fieldParentPtr("animal", a);
        std.debug.print("{s}: woof\n", .{self.name});
    }
};

27.5 Choosing a Pattern

Max performance, types known at compile time anytype / comptime generics Closed set of variants Tagged union + inline else

Open set, runtime plugins, heterogeneous collections VTable fat pointer

Intrusive structures, implementations embedding the interface