Unions

Unions

Cuando declaramos un union, definimos un tipo de dato que puede contener uno entre varios tipos posibles.

Esos tipos posibles que puede adoptar los declaramos en el cuerpo del union de la siguiente forma:


const NombreUnion = union {

     nombre_campo: tipo,

      …

     nombre_campoN: tipo,

}; 


Solo uno de estos tipos puede estar activo al mismo tiempo.

Union Simple (bare union)

La forma base que hemos indicado para declarar un union se denomina “unión simple” (bare union) y tiene un inconveniente: si el tipo activado, no se usa inmediatamente - por ejemplo lo devolvemos desde una función - no sabemos cuál de todos los tipos definidos fue activado.

Union etiquetado (Tagged union)

Para saber qué tipo está activo en cada momento, necesitamos declarar un union etiquetado (tagged union).

Esto se puede hacer de dos maneras:

Union etiquetado mediante enum previo

La primera manera consiste en declarar un enum previo que sirve para etiquetar el union:


const NombreEnumParaEtiquetado = enum {

     nombre_del_valor,

      …

     nombre_del_valor_N,

}; 

const NombreUnion = union(NombreEnumParaEtiquetado) {

     nombre_del_valor: tipo,

      …

     nombre_del_valorN: tipo,

}; 


En este tipo de “etiquetado” mediante un enum, el union tiene que llevar los campos con los mismos nombres definidos en el enum y especificar el tipo de cada uno.

Union etiquetado por inferencia

Si no queremos - o no necesitamos - definir el enum aparte para el etiquetado de los campos, podemos esta forma:


const NombreUnion = union(enum) { // <- palabra enum entre paréntesis

     nombre_campo: tipo,

      …

     nombre_campoN: tipo,

}; 


Al definir un union así, Zig inferirá automáticamente las etiquetas sin necesidad de definirlas en un enum separado.

Puedes instanciar un union sin tener que escribir su nombre, siempre que el dato al que lo asignas ya tenga ese tipo declarado:

  const MyUnion = union(enum){

    field1: int,

    field2: float,

    // ...

  }

  // con un .{ sin el nombre - literal anónimo

  const u_my1: MyUnion = .{ .field1 = 42 };

  // con el nombre explícito

  const u_my2: MyUnion = MyUnion{ .field2 = 13.14 };

La sintaxis - .{ } - se llama literal anónimo porque el valor se construye sin mencionar explícitamente el nombre del tipo que representa. También lo podemos usar en enums y como ya sabes lo usamos en los structs para crear tuplas.

Vamos a usar un union que hemos llamado EngineSocket para describir el sitio del ala donde puede montarse un motor- ese hueco podrá quedar vacío: .empty = true, o tener un motor montado: .engine=DroneEngine{… , pero no podrá tener ambas cosas a la vez.

union_2.zig

const std = @import("std");

const print = std.debug.print;

// errores posibles de motor

const EngineError = error{ Damaged, Autorepairing, NotConnected };

// posibles estados de un motor

const EngineStatus = enum(i8) {

    failure = -1, off = 0, on = 1, testing = 2, autorepairing = 3,

};

// motor como 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,

    // método que formatea el struct y se invoca

    // automáticamente cuando lo imprimimos

    pub fn format(self: DroneEngine, writer: anytype) !void {

        try writer.print(

            \\Motor {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,

    // lo que imprimimos depende del tipo de dato

    pub fn format(self: EngineSocket, writer: anytype) !void {

        switch (self) {

            .engine => |o_eng| {

                try writer.print("{f}", .{o_eng});

            },

            .empty => {

                try writer.print("-=VACIO=-", .{});

            },

        }

    }

};

const Wing = struct {

    const N_ENGINE_SOCKETS: u8 = 4;

    // al inicio tenemos 4 sitios para motores

    a_sockets: [Wing.N_ENGINE_SOCKETS]EngineSocket = .{

        EngineSocket{

            .empty = true,

        },

    } ** Wing.N_ENGINE_SOCKETS,

    // montar los motores en el ala

    fn mount_engines(self: *Wing, a_engines2mount: []const DroneEngine) void {

        // pasamos por todos los sockets

        for (0..Wing.N_ENGINE_SOCKETS) |n_i| {

            // si viene un motor para montar y si no dejamos vacío

            self.a_sockets[n_i] = if (n_i < a_engines2mount.len)

                .{ .engine = a_engines2mount[n_i] }

            else

                .{ .empty = true };

        }

    }

    // imprimimos el estado de los motores

    fn show_state(self: *Wing) void {

        print("-------------------------------------------------\n", .{});

        for (&self.a_sockets) |*o_engine_socket| {

            print("{f}\n", .{o_engine_socket});

        }

        print("\n{} fallos\n", .{DroneEngine.n_total_failed});

    }

};

// todos los motores disponibles

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 {

    // el ala vacía

    var o_wing = Wing{};

    //  mostrar el ala vacía

    o_wing.show_state();

    // montamos 4 de los motores disponibles

    o_wing.mount_engines(a_all_engines[0..2]);

    // mostramos el estado del ala

    o_wing.show_state();

}

$ zig run union_2.zig

-------------------------------------------------

-=VACIO=-

-=VACIO=-

-=VACIO=-

-=VACIO=-

0 fallos

-------------------------------------------------

Motor E1:

  * rpm: 0

  * health: 100

  * status: off

Motor E2:

  * rpm: 0

  * health: 95

  * status: off

-=VACIO=-

-=VACIO=-

0 fallos

En el ejemplo anterior, el jugador solo había montado 2 motores en el “ala”. ¿Sabrías encontrar el número que hay que cambiar para que, solo modificando ese dato se monten los 4 motores necesarios?

Sobrevuelan las olas del mar profundo, que de noche, en la oscuridad, reflejan la luz de la luna, como una enorme cota de malla hecha de plata. Nota la brisa caprichosa, - momento caricia, momento látigo - en la cara y los pies helados que, a veces, casi tocan la superficie del mar. A ratos planean; a ratos se encienden los motores con zumbido salvaje, olor a metal quemado, sufriendo para mantenerse en el aire. Se apagan cuando el viento les lleva a favor, y así siguen una ruta que ahora mismo solo 1KR conoce y visualiza desde dentro del ala.

Al aterrizar en la costa pedregosa, después del largo vuelo, siente el dolor en las manos agarrotadas, molido por el cansancio. Desenchufa y arranca la cabeza metálica desde el ala.


Errores
Funciones y Listas
© 2025 Zen of Zig