Unions
Unions allow us to create variables that can take on different data types depending on what we need. But only one of those types can be active at a time.
We declare those possible types in the body of the union like this:
const UnionName = union {
field_name: type,
…
field_nameN: type,
};
Only one of these types can be active at a time.
Simple Union (bare union)
The base form we’ve shown for declaring a union is called a bare union (simple union), and it has a drawback: if the active type isn’t used immediately, for example,
if we return it from a function, we don’t know which of the defined types was activated.
Tagged Union
To know which type is active at any given moment, we need to declare a tagged union.
This can be done in two ways:
Tagged Union via prior enum
The first way is to declare a prior enum that serves to tag the union:
const TaggingEnumName = enum {
value_name,
…
value_name_N,
};
const UnionName = union(TaggingEnumName) {
value_name: type,
…
value_nameN: type,
};
In this type of tagging via an enum, the union must have fields with the same names as the enum and specify the type for each.
Tagged Union by Inference
If we don’t want or don’t need to define a separate enum to tag the fields, we can use this form:
const UnionName = union(enum) { // <- palabra enum entre paréntesis
field_name: type,
…
field_nameN: type,
};
When defining a union like this, Zig will automatically infer the tags, so they don't need to be declared in a separate enum.
You can instantiate a union without writing its name, as long as the variable you're assigning it to already has that type declared:
|
const MyUnion = union(enum){ field1: int, field2: float, // ... } // with a .{ without the name - anonymous literal const u_my1: MyUnion = .{ .field1 = 42 }; // with the explicit name const u_my2: MyUnion = MyUnion{ .field2 = 13.14 }; |
The syntax .{ } is called an anonymous literal because the value is constructed without explicitly mentioning the name of the type it represents. We can also use it with enums, and as you already know, we use it with structs to create tuples.
We’re going to use a union called EngineSocket to describe the slot on the wing where an engine can be mounted-that slot can either be empty: .empty = true, or have a mounted engine: .engine=DroneEngine{… , but it can’t have both at the same time.
union_2.zig |
|
const std = @import("std"); const print = std.debug.print; // possible engine errors const EngineError = error{ Damaged, Autorepairing, NotConnected }; // possible engine states const EngineStatus = enum(i8) { failure = -1, off = 0, on = 1, testing = 2, autorepairing = 3, }; // engine as struct const DroneEngine = struct { var n_total_failed: u8 = 0; const N_MAX_RPM: u32 = 8500; const N_MIN_HEALTH: u32 = 75; e_status: EngineStatus, n_health: u8, s_key: []const u8, n_rpm: u32, // method that formats the struct and is invoked // automatically when we print it pub fn format(self: DroneEngine, writer: anytype) !void { try writer.print( \\Engine {s}: \\ * rpm: {} \\ * health: {} \\ * status: {s} , .{ self.s_key, self.n_rpm, self.n_health, @tagName(self.e_status), }); } }; const EngineSocket = union(enum) { engine: DroneEngine, empty: bool, // what we print depends on the data type pub fn format(self: EngineSocket, writer: anytype) !void { switch (self) { .engine => |o_eng| { try writer.print("{f}", .{o_eng}); }, .empty => { try writer.print("-=EMPTY=-", .{}); }, } } }; const Wing = struct { const N_ENGINE_SOCKETS: u8 = 4; // at the start, we have 4 engine slots a_sockets: [Wing.N_ENGINE_SOCKETS]EngineSocket = .{ EngineSocket{ .empty = true, }, } ** Wing.N_ENGINE_SOCKETS, // mount the engines on the wing fn mount_engines(self: *Wing, a_engines2mount: []const DroneEngine) void { // go through all the sockets for (0..Wing.N_ENGINE_SOCKETS) |n_i| { // if there's an engine to mount, otherwise leave empty self.a_sockets[n_i] = if (n_i < a_engines2mount.len) .{ .engine = a_engines2mount[n_i] } else .{ .empty = true }; } } // print the state of the engines fn show_state(self: *Wing) void { print("-------------------------------------------------\n", .{}); for (&self.a_sockets) |*o_engine_socket| { print("{f}\n", .{o_engine_socket}); } print("\n{} failures\n", .{DroneEngine.n_total_failed}); } }; // all available engines const a_all_engines = [_]DroneEngine{ DroneEngine{ .s_key = "E1", .n_health = 100, .n_rpm = 0, .e_status = .off }, DroneEngine{ .s_key = "E2", .n_health = 95, .n_rpm = 0, .e_status = .off }, DroneEngine{ .s_key = "E3", .n_health = 100, .n_rpm = 0, .e_status = .off }, DroneEngine{ .s_key = "E4", .n_health = 98, .n_rpm = 0, .e_status = .off }, }; pub fn main() void { // the empty wing var o_wing = Wing{}; // show the empty wing o_wing.show_state(); // mount 4 of the available engines o_wing.mount_engines(a_all_engines[0..2]); // show the state of the wing o_wing.show_state(); } |
|
$ zig run union_2.zig |
|
------------------------------------------------- -=EMPTY=- -=EMPTY=- -=EMPTY=- -=EMPTY=- 0 failures ------------------------------------------------- Engine E1: * rpm: 0 * health: 100 * status: off Engine E2: * rpm: 0 * health: 95 * status: off -=EMPTY=- -=EMPTY=- 0 failures |
In the previous example, the player had only mounted 2 engines on the "wing." Can you find the number that needs to be changed so that, by modifying just that value, the 4 necessary engines are mounted?
They fly over the waves of the deep sea, which at night, in the darkness, reflect the moonlight like a massive chainmail made of silver. He feels the capricious breeze-moment caress, moment whip-on his face and his frozen feet that sometimes graze the surface of the sea. At times they glide; at times the engines roar to life with a wild hum, the smell of burnt metal, suffering to stay airborne. Engines shut off when the wind is in their favor, and so they follow a route that, for now, only 1KR knows and visualizes from inside the wing.
Upon landing on the rocky shore after the long flight, he feels the pain in his cramped hands, crushed from exhaustion. He unplugs and pulls the metal head from the wing.
- We made it, kid. You okay?
- Yes, I’m here, father. We did it again.