Zig Comptime Reflection
Comptime reflection is a way for code to ask questions about a type while the program is being compiled. It lets one piece of code work with many types by checking what each type has.
Checking One Field
This program checks whether a Person type has a name field and an email field. Only the first check is true.
const std = @import("std");
const Person = struct { name: []const u8, age: u32 };
pub fn main() !void {
const out = std.io.getStdOut().writer();
try out.print("{} {}\n", .{ @hasField(Person, "name"), @hasField(Person, "email") });
}Choosing With A Check
This program prints a different line for a type with a z field and one without it. The Point type has no z field.
const std = @import("std");
const Point = struct { x: i32, y: i32 };
pub fn main() !void {
const out = std.io.getStdOut().writer();
if (@hasField(Point, "z")) {
try out.print("has z\n", .{});
} else {
try out.print("no z\n", .{});
}
}Checking Two Types
This program checks the same field name on two different types. One type has the field and the other does not.
const std = @import("std");
const Person = struct { name: []const u8, age: u32 };
const Point = struct { x: i32, y: i32 };
pub fn main() !void {
const out = std.io.getStdOut().writer();
try out.print("{} {}\n", .{ @hasField(Person, "age"), @hasField(Point, "age") });
}Example
const std = @import("std");
const Person = struct { name: []const u8, age: u32 };
const Point = struct { x: i32, y: i32 };
fn hasName(comptime T: type) bool {
return @hasField(T, "name");
}
pub fn main() !void {
const out = std.io.getStdOut().writer();
try out.print("{} {}\n", .{ hasName(Person), hasName(Point) });
}Key points
- Reflection: reflection is code that examines a type while it is being compiled.
- @hasField: @hasField is a builtin that returns true when a type has a field with a given name.
- A field name is written as text, inside quotes, after the type name.
- The answer is known at compile time, before the program runs.
- A program can check several fields and choose different behavior for each result.
- Reflection keeps code general, because one check works for any type that is passed in.
The helper takes a type and returns the result of the field check. It works for any type, because the check happens at compile time. The main function prints the helper result for two types.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does @hasField(Person, "name") return when Person has a name field?