This method is something I never thought of before. It’s great! If we adopt this approach, we don’t even need to introduce |T| grammar.
You can take a look at this example:
const std = @import("std");
pub fn satisfyTrait(comptime Trait: type, comptime T: type) R: {
if (@typeInfo(Trait) != .@"struct")
@compileError("Trait must be an 'struct'.");
break :R bool;
} {
const decls = std.meta.declarations(Trait);
inline for (decls) |decl| {
if (!std.meta.hasMethod(T, decl.name)) {
return false;
}
}
return true;
}
pub const Drawable = struct {
pub fn draw(self: *const Drawable) void {
_ = self;
@compileError("No impl");
}
};
pub fn draw(drawable: anytype) R: {
if (!satisfyTrait(Drawable, @TypeOf(drawable)))
@compileError("drawable must satisfy Drawable trait");
break :R void;
} {
drawable.draw();
}
pub const Circle = struct {
radius: f32,
pub fn draw(self: *const Circle) void {
std.debug.print("Circle(radius={})", .{self.radius});
}
};
pub fn main(init: std.process.Init) !void {
_ = init;
const circle = Circle{ .radius = 5.0 };
draw(circle);
}
I can completely decide on the logic of satisfyTrait. Even imitating the logic of Rust.
The purpose of all the methods I proposed is that when I call third-party libraries, I am not sure what the library author expects when using anytype here, so I hope to introduce a mechanism to inform me of its limitations. When writing restrictions as Zig code in the return type expression, I can directly see what type of value I should pass in through the IDE’s hover prompt. This must be one of the best encoding methods for Zig!!!
I only implemented a simple satisfyTrait above, and I expect the standard library to have a complete satisfyTrait. It not only checks whether the declaration exists, but also whether the declared type matches.
THIS IDEA IS REALLY GREAT!!!