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:
- std.meta.stringToEnum convierte un string en el enum equivalente
- @tagName convierte enum a string
- @intFromEnum convierte un enum a entero
- @enumFromInt convierte entero a enum
- std.meta.tags devuelve los valores de un enum como array
- std.meta.fields devuelve la información de un enum
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 |