zig-etiquetas-bucles

Etiquetas en Zig: labels para bucles, bloques y flujo

  • 3 min

Una etiqueta en Zig es un nombre que damos a un bucle o bloque para controlar desde dónde salimos o continuamos. Es útil cuando tienes un bucle dentro de otro y, al encontrar lo que buscas, quieres salir de todo.

En lenguajes como C o C++, la palabra clave break solo rompe el bucle más interno. Para salir de ambos, solemos recurrir a trucos “sucios” como:

  1. Crear una variable booleana encontrado = true y comprobarla en el bucle externo.
  2. Usar un goto 😱.
  3. Refactorizar el código en una función aparte y usar return.

Zig ofrece una solución explícita y legible: las etiquetas (labels).

Sintaxis de las etiquetas

Una etiqueta en Zig es simplemente un identificador seguido de dos puntos :, que se coloca justo antes de una sentencia de bucle (while, for) o un bloque.

nombre_etiqueta: while (condicion) {
    ...
}
Copied!

Una vez etiquetado el bucle, podemos usar break :nombre_etiqueta o continue :nombre_etiqueta para referirnos específicamente a ese bucle, sin importar lo profundo que estemos anidados.

Rompiendo bucles anidados

Imaginemos que estamos buscando un valor específico en una matriz de 10x10.

const matriz = [3][3]u8{
    .{ 1, 2, 3 },
    .{ 4, 5, 6 },
    .{ 7, 8, 9 },
};

pub fn main() void {
    const objetivo = 5;
    
    // Etiquetamos el bucle exterior
    bucle_externo: for (matriz, 0..) |fila, i| {
        for (fila, 0..) |valor, j| {
            if (valor == objetivo) {
                std.debug.print("Encontrado en [{d}, {d}]\n", .{i, j});
                
                // Rompemos directamente el bucle principal
                break :bucle_externo;
            }
        }
    }
    std.debug.print("Búsqueda terminada.\n", .{});
}
Copied!

Si hubiéramos usado un break normal (sin etiqueta), solo habríamos salido del for interior, y el bucle externo habría seguido buscando en la siguiente fila innecesariamente.

Esta sintaxis mantiene el “Zen de Zig”: el flujo es explícito. Al leer break :bucle_externo, sabes exactamente a dónde va a saltar la ejecución, sin tener que rastrear llaves de cierre.

Etiquetas en continue

Lo mismo aplica para continue. Si estamos en un bucle interno y queremos forzar la siguiente iteración del bucle externo, usamos la etiqueta.

procesamiento: for (items) |item| {
    while (condicion_compleja) {
        // ... lógica ...
        
        if (item_es_invalido) {
            // Deja de procesar este item y pasa al siguiente del for
            continue :procesamiento;
        }
    }
}
Copied!

Etiquetas en bloques

Las etiquetas no son exclusivas de los bucles. También podemos etiquetar un bloque de código { ... }.

Esto permite inicializar constantes cuya lógica necesita varios pasos.

Si quieres calcular una constante const, pero la lógica requiere variables temporales o varias condiciones, en otros lenguajes podrías necesitar una función auxiliar o una expresión invocada inmediatamente.

En Zig, usamos un bloque etiquetado:

const numero_complejo = bloque_calculo: {
    var x: i32 = 10;
    x += 5;
    
    if (x > 20) {
        // "Rompemos" el bloque devolviendo un valor
        break :bloque_calculo 100;
    }
    
    // Valor por defecto del bloque
    break :bloque_calculo x * 2;
};
Copied!

La sintaxis importante es break :etiqueta valor;. Esto funciona igual que un return en una función, pero para un bloque local.

  1. El código dentro del bloque se ejecuta.
  2. Las variables x mueren al salir del bloque (no contaminan el ámbito).
  3. El resultado se asigna a numero_complejo de forma inmutable (const).

Esta técnica es la forma idiomática en Zig de emular las expresiones complejas que en Rust se hacen simplemente devolviendo el último valor del bloque.