Here’s something I was messing around with a while back. It works in current Zig without any changes needed to the language.
const std = @import("std");
pub fn main() void {
const foo = Foo{};
// Succeeds as `Foo` is a `Printable`
print(@TypeOf(foo), foo);
// Fails as `Foo` is not a `Writeable`
//write(@TypeOf(foo), foo);
}
// My custom type
const Foo = struct {
// Sample fields that are ignored by the interface
a: i32 = 0,
b: void = {},
// Implements `print` for the printable interface
pub fn print(self: Foo) void {
std.debug.print("Foo.print: {}\n", .{self});
}
};
// Generic functions
fn print(comptime T: type, printer: Printable(T)) void {
printer.print();
}
fn write(comptime T: type, writer: Writeable(T)) void {
writer.write();
}
// Interfaces
fn Printable(comptime T: type) type {
const interface = struct {
print: fn (T) void,
};
if (conformsTo(interface, T)) return T;
@compileError("Type " ++ @typeName(T) ++ " does not conform to the Printable interface");
}
fn Writeable(comptime T: type) type {
const interface = struct {
write: fn (T) void,
};
if (conformsTo(interface, T)) return T;
@compileError("Type " ++ @typeName(T) ++ " does not conform to the Writeable interface");
}
/// Checks if `T` conforms to the `Interface` protocol.
fn conformsTo(comptime Interface: type, comptime T: type) bool {
if (@typeInfo(Interface) != .@"struct") @compileError("Interface must be a struct");
outer: for (std.meta.fields(Interface)) |field| {
if (@hasDecl(T, field.name)) {
if (field.type == @TypeOf(@field(T, field.name))) continue;
return false;
}
for (std.meta.fields(T)) |field2| {
if (std.meta.eql(field.name, field2.name)) {
if (field.type == field2.type) continue :outer;
return false;
}
}
// Has neither a field nor a declaration with the given name
return false;
}
return true;
}
The obvious downside is that every time you call write
or print
you have to pass in the type as the first argument, but otherwise I quite like it.
Sharing as I had fun playing around with this and you might too. I’m not sure if it’s a good way of doing things since I never used it in a serious project.