Printing Zig Structs - Edward Loveall

15 Likes

Good article. I think that current fmt has yet to reach its final form and because it’s userland code it’s not being prioritized yet.

What the ultimate fmt code will look like, I don’t know, but I think there’s space for some optimism if you ever encountered an annoying limitation with it.

5 Likes

Nice! Given the title, I was wondering if you could mention the third common way, using {f} ?

const Greeting = struct {
    name: []const u8,
    time: []const u8,

    pub fn format(g: Greeting, w: *std.Io.Writer) std.Io.Writer.Error!void {
        try w.print("greetings {s}\n", .{g.name});
    }
};

std.debug.print("{f}\n", .{greeting});
5 Likes

You know, I never considered this pattern! As I alluded to in the article, I cut my teeth on a much higher level languages so some of the more common patterns are totally unknown to me. Everyday someone is born who doesn’t know how to allocate memory… or something :smile:

I’d encourage you to also write a short blog post either as an explicit or implicit response. I really want to see more zig patterns.

1 Like

All good, just figured I’d mention it since I bet yours will show up when people search about printing zig structs, or if you wanna write a follow-up one day. As for the response suggestion, nah, you write really well, I don’t :slight_smile:

Honestly,

try stdout.interface.print("Good {[time]s}, {[name]s}!\n", greeting);

this kind of thing is a lifesaver when you have to duplicate the arguments and their order is all over the place.
I would like to see some sort of a tree structure option when printing a struct with {any} that would be a goat feature i think.

2 Likes

Nothing new to add except to say that is one of the most aesthetically pleasing blogs I’ve ever seen.

2 Likes

That’s very kind, thank you. I was thinking it needs a bit of design nudge, but perhaps not as much as I thought!

3 Likes

adding to this: you can use std.fmt.Alt to “add”/“overwrite” a format function to an existing type e.g:

const Greeting = struct {
    name: []const u8,
    time: []const u8,

    
};

fn formatGreeting(g: Greeting, w: *std.Io.Writer) std.Io.Writer.Error!void {
    try w.print("greetings {s}\n", .{g.name});
}
std.debug.print("{f}\n", .{std.fmt.Alt(Greet, formatGreeting){.data = greeting}});

there is also std.fmt.alt, lowercase a (confusing ik), to call a different function on the type e.g:

const Greeting = struct {
    name: []const u8,
    time: []const u8,

    
    fn superFormat(g: Greeting, w: *std.Io.Writer) std.Io.Writer.Error!void {
        try w.print("SUPER greetings {s}\n", .{g.name});
    }
};

std.debug.print("{f}\n", .{std.fmt.alt(greetings, .superFormat)});

Personally I think the following would be a better implementation, that is more flexible but still nice:

fn alt(ctx: anytype, comptime f: fn(@TypeOf(ctx), *Io.Writer) Error!void) Alt(@TypeOf(ctx), f) {
    return .{ .data = ctx };
}
// ..

std.debug.print("{f}\n", .{alt(greetings, Greetings.superFormat)});
5 Likes