Enums

para Zig 0.15.2 Comprar

Enums

Los enums nos permiten definir tipos de datos con un conjunto limitado de valores, a los que asignamos un nombre.


const NombreDelEnum = enum {

     nombre_del_valor,

      …

     nombre_del_valor_N,

}; 


En el caso de un motor, recién desensamblado de un dron, conectado a nuestros instrumentos de medida, podríamos definir dos estados principales: encendido y apagado.

enum_e_status.zig

const std = @import("std");

const print = std.debug.print;

const EngineStatus = enum {

    on,

    off,

};

pub fn main() void {

    const e_status_1 = EngineStatus.off;

    print("Estado del motor 1: {}\n", .{e_status_1});

}

$ zig run enum_e_status.zig

Estado del motor 1: .off

Si solo tenemos estos dos estados, nos bastaría con un bit para representarlos. Podemos especificar el tipo de entero que se va a usar para almacenar el valor del enum:

  const EngineStatus = enum(u1) {

    on,

    off,

  };

Si definimos el tipo de entero, hay que tener en cuenta su rango:

  const EngineStatus = enum(u1) {

    on,

    off,

    unknown, // <- este ya no cabe en un bit

  };

error: enumeration value '2' too large for type 'u1'

Los valores ordinales de los enums comienzan en 0. Podemos acceder a ellos usando:

  @intFromEnum(EngineStatus.off) // -> 1

También podemos definir los valores de forma explícita:

  const EngineStatus = enum(i8) { // i8 para poder incluir valores negativos

    on = 1,

    off = 0,

    failure = -1,

  };

Si no definimos un valor ordinal explícito después de haber definido otros a mano, Zig va a intentar asignarlo a partir del último valor definido:

  const EngineStatus = enum(i8) {

    on = 1,

    off = 0,

    failure = -1,

    testing, // añadido sin asignar a mano

  };

Esto provocará un error ya que el compilador intentará asignar a testing el valor 0 y ese valor ya está ocupado por off:

error: enum tag value 0 already taken

    testing,

    ^~~~~~~

Para arreglarlo basta con poner las definiciones en el orden correcto:

  // los valores manuales en orden

  const EngineStatus = enum(i8) {

    failure = -1,

    off = 0,

    on = 1,

    testing,// añadido sin asignar a mano

  };

Enums no exhaustivos

Mira esta manera de construir un enum:

  const EngineStatus = enum(i8) { <- obligatorio el tipo de tag

    off = 0,

    on = 1,

    failure = -1,

    _, // <- indica que es no exhaustivo

  };

Hasta ahora siempre hemos declarado enums exhaustivos, en los que se definen todos los valores. Pero al usar _ y un tipo explícito como i8, hemos definido un enum no exhaustivo.

enum_non_exhaustive.zig

const std = @import("std");

const print = std.debug.print;

const EngineStatus = enum(u8) { // -> obligatorio el tipo de tag

    off,

    on,

    failure,

    _, // <- indica que es no exhaustivo

};

pub fn main() void {

    // valor normal con un tag declarado

    const e_1: EngineStatus = .on;

    // legal, aunque 33 no es un tag declarado

    const e_2: EngineStatus = @enumFromInt(33);

    print(

        "status 1 = {}, int = {}\n",

        .{ e_1, @intFromEnum(e_1) },

    );

    print(

        "status 2 = {}, int = {}\n",

        .{ e_2, @intFromEnum(e_2) },

    );

    // tiene que cubrir valores con else

    switch (e_2) {

        .off => print("engine off\n", .{}),

        .on => print("engine on\n", .{}),

        // .failure => print("engine failure\n", .{}),

        // si omitimos .failure, _ => no es suficiente

        // necesitamos else

        else => |n_val| print("other value: {}\n", .{n_val}),

        // si solo incluyes _ te dará error

        // porque .failure fue declarado pero no cubierto

        _ => |n_val| print("undeclared value: {}\n", .{n_val}),

    }

}

$ zig run enum_nonexhaustive.zig

status 1 = .on, int = 1

status 2 = @enumFromInt(33), int = 33

undeclared value: @enumFromInt(33)

Fíjate en el rango posible del u8:  desde 0  hasta 255. Ahora, al declarar un enum no exhaustivo, Zig ya no puede asumir que hemos cubierto todo el rango. Si hacemos un switch sobre este tipo, Zig nos obliga a incluir un else (para valores no cubiertos) o _ (valores sin tag) o ambos.

  // cubrir los valores con else

  switch (e_status) {

    .off => ...,

    .on => ...,

    // si omitimos alguno, no basta con _ =>,

    // necesitamos else

    else => |n_val| ... // else obligatorio

    _ => |n_val| ... // solo valores no declarados

  }

También, es interesante que podemos forzar un valor no declarado así:

  const e_status_x: EngineStatus = @enumFromInt(33);

  // legal, aunque 33 no es un tag declarado

Esto es útil si, por ejemplo, el valor que convertimos proviene de un archivo, pero también implica tener mucho cuidado al validarlo.  En un principio, los enums no exhaustivos tal vez sean un tema algo avanzado para empezar a programar con Zig, así que los veremos en los próximos libros de la serie.

Variables y constantes dentro del enum

Podemos declarar variables y constantes dentro de los enum. Estas pertenecen al tipo en sí - su ámbito (scope) será global y su valor no estará vinculado a una instancia.

enum_2.zig

const std = @import("std");

const print = std.debug.print;

const EngineStatus = enum(u2) {

    // esta variable da el valor para el tipo

    // no para las instancias individuales

    var n_starts: u32 = 0;

    on,

    off,

    failure,

};

pub fn main() void {

    const e_status1 = EngineStatus.on;

    // acceso global, cambia el dato del tipo

    EngineStatus.n_starts += 1;

    var e_status2 = EngineStatus.on;

    // acceso global

    EngineStatus.n_starts += 1;

    print("Estado del motor 1: {}\n", .{e_status1});

    e_status2 = EngineStatus.off;

    print("Estado del motor 2: {}\n", .{e_status2});

    print("Contador de arranques de motores: {}\n", .{EngineStatus.n_starts});

}

$ zig run enum_2.zig

Estado del motor 1: .on

Estado del motor 2: .off

Contador de arranques de motores: 2

Métodos del enum

Podemos definir funciones dentro de un enum. Cuando una función forma parte de un enum, de un struct o de un union se llama método. No dependen de una instancia específica, salvo cuando reciben una instancia como parámetro (self).

enum_6engines.zig

const std = @import("std");

const print = std.debug.print;

const EngineStatus = enum(u3) {

    var n_can_start: u32 = 0; // dato del tipo EngineStatus

    failure,

    off,

    on,

    autorepairing,

    // método de una instancia específica (recibe self)

    fn can_start(self: EngineStatus) bool {

        return switch (self) {

            .autorepairing, .failure => false,

            else => true,

        };

    }

    // método del tipo (no recibe self)

    fn count_can_start() void {

        EngineStatus.n_can_start += 1;

    }

};

pub fn main() void {

    const a_engines = [_]EngineStatus{

        .off,

        .off,

        .on,

        .autorepairing,

        .off,

        .failure,

    };

    for (a_engines) |e_status| {

        if (e_status.can_start()) EngineStatus.count_can_start();

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

    }

    print("Hay {} motores disponibles.\n", .{EngineStatus.n_can_start});

}

$ zig run enum_6engines.zig

.off

.off

.on

.autorepairing

.off

.failure

Hay 4 motores disponibles.

Funciones interesantes para manejar los enums:

enum_values.zig

const std = @import("std");

const print = std.debug.print;

const EngineStatus = enum(i8) {

    failure = -1,

    off = 0,

    on = 1,

    testing, // 2

    autorepairing, // 3

};

pub fn main() void {

    // std.meta.fields - información de campos

    print("\nNúmero de estados: {any}\n", .{std.meta.fields(EngineStatus).len});

    // std.meta.tags - todos los valores del enum como array

    print("Todos los estados posibles detectados:\n", .{});

    for (std.meta.tags(EngineStatus)) |e_status| {

        print(" {} - \"{s}\" = {d}\n",

            // estado

            .{

                e_status,

                @tagName(e_status),

                @intFromEnum(e_status),

            });

    }

}

$ zig run enum_values.zig

Número de estados: 5

Todos los estados posibles detectados:

 .failure - "failure" = -1

 .off - "off" = 0

 .on - "on" = 1

 .testing - "testing" = 2

 .autorepairing - "autorepairing" = 3

ARP1 dron - Daedalos Labs

Enums, Structs, Errores, Unions
Structs
© 2025 - 2026 Zen of Zig