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.
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});
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 ![]()
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.
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 ![]()
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.
Nothing new to add except to say that is one of the most aesthetically pleasing blogs I’ve ever seen.
That’s very kind, thank you. I was thinking it needs a bit of design nudge, but perhaps not as much as I thought!
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)});