Tagged Unions
A tagged union combines a union with a tag that keeps track of which field is active.
const std = @import("std");
const print = std.debug.print;
const ValueTag = enum {
int,
float,
boolean,
};
const Value = union(ValueTag) {
// the fields here have to match those in the enum
int: i64,
float: f64,
boolean: bool,
};
pub fn main() void {
var value = Value{ .int = 4 };
print("int value: {}\n", .{value.int});
value = .{ .boolean = true };
print("boolean value: {}\n", .{value.boolean});
// A tagged union can be cast to its tag.
const tag: ValueTag = value;
print("tag is {}\n", .{tag});
// Access the fields with a switch:
switch (value) {
.int => |i| print("int: {d}\n", .{i}),
.float => |f| print("float: {d:.2}\n", .{f}),
.boolean => |b| print("boolean: {}\n", .{b}),
}
// Modify them using pointers:
switch (value) {
.int => |*i| i.* += 1,
.float => |*f| f.* += 1.0,
.boolean => |*b| b.* = !b.*,
}
print("boolean value: {}\n", .{value.boolean});
}$ zig run tagged.zig
int value: 4
boolean value: true
tag is .boolean
boolean: true
boolean value: false
Inferred tag type
The compiler can infer the tag type for us:
const std = @import("std");
const print = std.debug.print;
const Value = union(enum) {
int: i64,
float: f64,
boolean: bool,
fn to_float(self: Value) f64 {
return switch (self) {
.int => |i| @floatFromInt(i),
.float => |f| f,
.boolean => |b| if (b) 1.0 else 0.0,
};
}
};
pub fn main() void {
const value = Value{ .int = 4 };
print("as float: {d:.2}\n", .{value.to_float()});
}$ zig run tagged-2.zig
as float: 4.00
Next example: Allocators.