const std = @import("std"); // base struct const Animal = struct { // points to the derived struct ctx: *anyopaque, // points to the vtable of the concrete type vtable: *const VTable, // the vtable interface derived struct must implement const VTable = struct { make_noise: *const fn (ctx: *anyopaque, loudness: u32) anyerror!void, }; // call the derived struct's implementation pub fn make_noise(animal: Animal, loudness: u32) anyerror!void { return animal.vtable.make_noise(animal.ctx, loudness); } }; const Dog = struct { // extra data weight: u32, // implement the interface const vtable = Animal.VTable{ .make_noise = &make_noise, }; pub fn make_noise(ctx: *anyopaque, loudness: u32) anyerror!void { const dog: *Dog = @alignCast(@ptrCast(ctx)); std.debug.print("woof {} {}\n", .{ dog.weight, loudness }); } // helper to convert to the base struct pub fn _animal(self: *Dog) Animal { return Animal{ .ctx = @ptrCast(self), .vtable = &vtable, }; } }; const Cat = struct { weight: u32, const vtable = Animal.VTable{ .make_noise = &make_noise, }; pub fn _animal(self: *Cat) Animal { return Animal{ .ctx = @ptrCast(self), .vtable = &vtable, }; } pub fn make_noise(ctx: *anyopaque, loudness: u32) anyerror!void { const cat: *Cat = @alignCast(@ptrCast(ctx)); std.debug.print("meow {} {}\n", .{ cat.weight, loudness }); } }; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const alloc = gpa.allocator(); // list of base structs var animal_list = std.ArrayList(Animal).init(alloc); defer { for (animal_list.items) |animal| { if (animal.vtable == &Dog.vtable) { const dog: *Dog = @alignCast(@ptrCast(animal.ctx)); alloc.destroy(dog); } else if (animal.vtable == &Cat.vtable) { const cat: *Cat = @alignCast(@ptrCast(animal.ctx)); alloc.destroy(cat); } } animal_list.deinit(); } for (0..20) |i| { if (i % 2 == 0) { var dog = try alloc.create(Dog); dog.* = Dog{ .weight = @intCast(i) }; try animal_list.append(dog._animal()); } else { var cat = try alloc.create(Cat); cat.* = Cat{ .weight = @intCast(i) }; try animal_list.append(cat._animal()); } } // meows and woofs here for (animal_list.items) |animal| { try animal.make_noise(10); } }
const Animal = union(enum) { cat: Cat, dog: Dog, pub fn make_noise(self: Animal) void { switch (self) { inline else => |impl| impl.make_noise(), } } };