En Zig, la función @tagName es una herramienta de tiempo de compilación que devuelve el nombre textual de una etiqueta de un valor enum. Aunque su uso más evidente suele ser en la generación de mensajes de error o en la introspección de tipos, se vuelve particularmente útil cuando se necesita depurar código sin introducir dependencias externas o sin generar salidas excesivas.
Comprendiendo @tagName
La sintaxis básica es muy sencilla:
const color = Color.Red;
const name = @tagName(color); // "Red"
Observa cómo se puede obtener una representación legible del enum en tiempo de compilación, lo que permite que el compilador lo incruste directamente en los mensajes o en las estructuras de datos que se impriman en tiempo de ejecución.
Ventajas de usar @tagName para depuración
- Sin sobrecarga en tiempo de ejecución: La conversión ocurre en tiempo de compilación, por lo que no hay coste de CPU ni memoria adicional.
- Mensajes claros: Los logs incluyen nombres legibles en lugar de valores numéricos.
- Portabilidad: No requiere librerías externas; Zig lo provee de forma nativa.
Ejemplo 1: Imprimir enums con @tagName
const std = @import("std");
// Definición de un enum simple
const Mood = enum {
Happy,
Sad,
Angry,
};
pub fn main() !void {
// Creamos una variable de tipo Mood
const currentMood = Mood.Sad;
// Usamos std.debug.print para mostrar el enum con su nombre
// std.debug.print utiliza el formateo similar a printf
std.debug.print("El estado actual es: {s}\n", .{ @tagName(currentMood) });
// Alternativamente, si queremos un log persistente:
const log = std.log.scoped(.app);
log.info("Estado: {s}", .{ @tagName(currentMood) });
}
Desglose línea por línea:
const std = @import("std");– Importa el paquete estándar.- Definimos el enum
Moodcon tres posibles valores. - En
main, inicializamoscurrentMoodconMood.Sad. - El llamado a
std.debug.printimprime el nombre del enum gracias a@tagName. - Se muestra también cómo usar el sistema de logs de Zig para información persistente.
Ejemplo 2: Depuración de structs con campos enum
const std = @import("std");
const Status = enum {
Ok,
Warning,
Error,
};
const Response = struct {
code: i32,
message: []const u8,
status: Status,
};
pub fn main() !void {
const res = Response{
.code = 404,
.message = "Not Found",
.status = Status.Error,
};
// Imprimimos cada campo, convirtiendo el enum a nombre legible
std.debug.print(
\\Respuesta recibida:
\\ Código: {d}
\\ Mensaje: {s}
\\ Estado: {s}
\\
, .{
res.code,
res.message,
@tagName(res.status), // Convierte Status.Error a "Error"
});
}
Notas explicativas:
- El struct
Responsecontiene un campo enumstatus. - La cadena formateada dentro de
std.debug.printutiliza\\para saltos de línea. - Se emplea
@tagName(res.status)para mostrar el nombre del estado.
Ejemplo 3: Uso en un switch con depuración condicional
const std = @import("std");
const Action = enum {
Start,
Stop,
Pause,
};
pub fn main() !void {
const act = Action.Pause;
// Switch que ejecuta diferentes bloques y muestra el nombre
switch (act) {
.Start => std.debug.print("Acción: {s}\n", .{ @tagName(act) }),
.Stop => std.debug.print("Acción: {s}\n", .{ @tagName(act) }),
.Pause => std.debug.print("Acción: {s}\n", .{ @tagName(act) }),
}
}
En este ejemplo, aunque cada rama del switch haga lo mismo, se ilustra cómo @tagName puede sustituir la necesidad de escribir manualmente los nombres de las etiquetas.
Ejemplo 4: Generar mensajes de error personalizados con @tagName
Cuando se necesita lanzar un error con información de un enum, @tagName permite crear mensajes más comprensibles:
const std = @import("std");
const FileError = error {
NotFound,
PermissionDenied,
Unknown,
};
const FileState = enum {
Closed,
Open,
Locked,
};
fn openFile(state: FileState) FileError!void {
// Simulamos un escenario donde el archivo debe estar abierto
if (state != FileState.Open) {
// Usamos @tagName para incluir el nombre del estado en el error
return error.NotFound; // En un caso real, se usaría un error personalizado
}
// Código para abrir el archivo...
}
pub fn main() !void {
// Intentamos abrir con estado cerrado
const res = openFile(FileState.Closed);
switch (res) {
error.NotFound => std.debug.print(
\\Error: {s} - El archivo no está en estado {s}
\\
, .{ @tagName(res), @tagName(FileState.Closed) }),
else => {},
}
}
Comentario de línea a línea:
- Se define un error enum
FileErrorcon varios casos. - El enum
FileStatemodela el estado del archivo. - La función
openFilecomprueba si el estado esOpen; si no, devuelveNotFound. - En
main, se intenta abrir con un estado incorrecto. - El
