Unions

for Zig 0.15.2 Buy

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.


Errors
Chapter Summary
© 2025 - 2026 Zen of Zig