Zig Comptime Serialization
Comptime serialization is the conversion of a struct into text, guided by its fields at compile time. The field list is known while compiling, so one function can write every field of any struct.
Serializing A Point
This program writes each field of a point on its own line. The point holds 3 and 4.
const std = @import("std");
const Point = struct { x: i32, y: i32 };
fn serialize(out: anytype, s: anytype) !void {
const T = @TypeOf(s);
inline for (@typeInfo(T).@"struct".fields) |field| {
if (field.type == []const u8) {
try out.print("{s}={s}\n", .{ field.name, @field(s, field.name) });
} else {
try out.print("{s}={}\n", .{ field.name, @field(s, field.name) });
}
}
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const p = Point{ .x = 3, .y = 4 };
try serialize(out, p);
}Serializing A Book
This program writes a book with a text field and a number field. Each field uses the format its type needs.
const std = @import("std");
const Book = struct { title: []const u8, pages: u32 };
fn serialize(out: anytype, s: anytype) !void {
const T = @TypeOf(s);
inline for (@typeInfo(T).@"struct".fields) |field| {
if (field.type == []const u8) {
try out.print("{s}={s}\n", .{ field.name, @field(s, field.name) });
} else {
try out.print("{s}={}\n", .{ field.name, @field(s, field.name) });
}
}
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const b = Book{ .title = "Dune", .pages = 412 };
try serialize(out, b);
}Serializing A Pet
This program writes a pet with a name and a leg count. The text field and the number field each use their own format.
const std = @import("std");
const Pet = struct { name: []const u8, legs: u32 };
fn serialize(out: anytype, s: anytype) !void {
const T = @TypeOf(s);
inline for (@typeInfo(T).@"struct".fields) |field| {
if (field.type == []const u8) {
try out.print("{s}={s}\n", .{ field.name, @field(s, field.name) });
} else {
try out.print("{s}={}\n", .{ field.name, @field(s, field.name) });
}
}
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const p = Pet{ .name = "rex", .legs = 4 };
try serialize(out, p);
}Example
const std = @import("std");
const Book = struct { title: []const u8, pages: u32 };
const Point = struct { x: i32, y: i32 };
fn serialize(out: anytype, s: anytype) !void {
const T = @TypeOf(s);
inline for (@typeInfo(T).@"struct".fields) |field| {
if (field.type == []const u8) {
try out.print("{s}={s}\n", .{ field.name, @field(s, field.name) });
} else {
try out.print("{s}={}\n", .{ field.name, @field(s, field.name) });
}
}
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
const b = Book{ .title = "Dune", .pages = 412 };
const p = Point{ .x = 3, .y = 4 };
try serialize(out, b);
try serialize(out, p);
}Key points
- Comptime serialization: comptime serialization is the conversion of a struct into text using its compile-time field list.
- Serialization: serialization is the act of writing a value in a form that can be stored or sent.
- Each field becomes one line with its name, an equals sign, and its value.
- Text fields and number fields need different print formats, and the field type decides which one is used.
- One generic function can serialize many structs, because the field list comes from the type itself.
- Serialized text is easy to read, because the field names stay beside their values.
The serializer walks the fields of any struct it is given. It picks a text format for text fields and a number format for the rest. The main function serializes a book and a point with the same function.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
How does a comptime serializer know which fields a struct has?