Enums

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

  };

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

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

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