Advanced Formatting
const std = @import("std");
const expect = std.testing.expect;
const Point = struct {
x: i32,
y: i32,
// A `format` method taking `*std.Io.Writer` is used by the `{f}`
// specifier. Older Zig passed a fmt string and options too; that
// signature no longer applies.
pub fn format(self: Point, writer: *std.Io.Writer) std.Io.Writer.Error!void {
try writer.print("({d}, {d})", .{ self.x, self.y });
}
};
const Colour = enum { red, green, blue };
test "{f} calls a custom format method" {
var buf: [64]u8 = undefined;
const text = try std.fmt.bufPrint(&buf, "{f}", .{Point{ .x = 1, .y = 2 }});
try expect(std.mem.eql(u8, text, "(1, 2)"));
}
test "{t} prints an enum tag name" {
var buf: [64]u8 = undefined;
const text = try std.fmt.bufPrint(&buf, "{t}", .{Colour.green});
try expect(std.mem.eql(u8, text, "green"));
}
test "{any} falls back to a structural dump" {
var buf: [128]u8 = undefined;
const text = try std.fmt.bufPrint(&buf, "{any}", .{[_]u8{ 1, 2, 3 }});
// Exact spelling is not contractual, but it contains the elements.
try expect(std.mem.indexOf(u8, text, "1") != null);
}
test "format strings are checked at compile time" {
// Passing the wrong number of arguments, or a specifier the type does
// not support, is a compile error, not a runtime surprise.
var buf: [32]u8 = undefined;
const ok = try std.fmt.bufPrint(&buf, "{d} {s}", .{ 1, "two" });
try expect(std.mem.eql(u8, ok, "1 two"));
}
test "writing to a list" {
const gpa = std.testing.allocator;
var out: std.Io.Writer.Allocating = .init(gpa);
defer out.deinit();
try out.writer.print("{f}", .{Point{ .x = 3, .y = 4 }});
try expect(std.mem.eql(u8, out.written(), "(3, 4)"));
}Custom format methods changed shape
Zig 0.15 rewrote this. The method now takes just a writer:
pub fn format(self: Point, writer: *std.Io.Writer) std.Io.Writer.Error!void {
try writer.print("({d}, {d})", .{ self.x, self.y });
}
The old signature (which also received the format string and an options
struct) no longer applies. If you find a tutorial showing
format(self, comptime fmt: []const u8, options: FormatOptions, writer: anytype),
it predates the change.
{f} calls it
A custom format method is not used by {} or {any}. You must ask for
it with {f}:
try writer.print("{f}", .{point}); // (1, 2)
{t} for tag names
{t} prints an enum’s tag name or an error’s name, replacing the older
practice of calling @tagName at every call site:
try writer.print("{t}", .{Colour.green}); // green
Composing writers
std.Io.Writer.Allocating gives you a growable in-memory writer, which is the
usual way to build a string from formatted pieces:
var out: std.Io.Writer.Allocating = .init(gpa);
defer out.deinit();
try out.writer.print("{f}", .{point});
out.written(); // []const u8