I want to keep a log of changes to some data. This change log can contain 3 types of messages: Insert, Update, and Delete. This naturally fits into a tagged union structure:
const Change = union(enum) {
Insert: struct { index: u32, value: Value },
Update: struct { index: u32, value: Value },
Delete: struct { index: u32 },
};
However, at comptime, the type of the change (aka the tag) is always known! Imagine that there is an API function that corresponds to each change to make this more apparent (see example logInsert
function below).
const ChangeLog = struct {
// Implementation omitted for brevity
pub fn logInsert(self: *Self, index: u32, value: Value) void {
// `logChange` writes the change to the log
self.logChange(.{ .Insert = .{ .index = index, .value = value } });
}
fn logChange(self: *Self, change: Change) void {
// not shown
}
}
It is theoretically possible for the logChange
function to know the tag of the change
at comptime and therefore emit a specialized function for each type at change during compilation. Is there a way to do this using a tagged union?
I know it can be accomplished the following way but it feels less straightforward than using a tagged union:
// Assume there is an enum type `ChangeCode` that is the tag type of the `Change` union
fn logChange(self: *Self, comptime code: ChangeCode, payload: ChangePayload(code)) void {
// not shown
}
fn ChangePayload(comptime code: ChangeCode) type {
return switch (code) {
.Insert => struct { index: u32, value: Value },
.Update => struct { index: u32, value: Value },
.Delete => struct { index: u32 },
};
}