Traits for Static Dispatch

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!!!

1 Like

Just pointing out that this idea (and evolutions of it) have been discussed several times over the past few years:

In fact, the std lib used to have a few implementations of this approach, but they were deemed unsatisfactory and better handled by the compiler per the core team (if I remember correctly).

My personal take is that the syntax proposed here would improve both readability and DX in a standardized way.

4 Likes

I fully understand why this scheme can be unsatisfactory, because it takes up the return type position, which is obviously not a clear convention. I believe that ideally there should be a compile-time block considered part of the function signature to include these constraints. But I have always disagreed that this constraint syntax should occupy the position of the parameter type (the original anytype position), as the type of constraints that can be expressed this way is too limited.