Cheatsheet: Iterator em Zig

Iterator em Zig

O padrão Iterator fornece uma maneira de acessar os elementos de uma coleção sequencialmente sem expor sua representação interna. Em Zig, o protocolo de iteração é simples e convencionado: uma struct com um método next() que retorna ?T (optional do tipo do elemento). Quando retorna null, a iteração acabou.

Quando Usar

  • Percorrer coleções customizadas
  • Processamento lazy (sob demanda) de sequências
  • Composição de transformações sem alocação intermediária
  • Gerar sequências infinitas ou muito grandes

Protocolo de Iteração em Zig

const std = @import("std");

// Qualquer struct com next() -> ?T funciona com while
const Contador = struct {
    atual: u32,
    maximo: u32,

    pub fn init(maximo: u32) Contador {
        return .{ .atual = 0, .maximo = maximo };
    }

    pub fn next(self: *Contador) ?u32 {
        if (self.atual >= self.maximo) return null;
        const valor = self.atual;
        self.atual += 1;
        return valor;
    }
};

pub fn main() void {
    var contador = Contador.init(5);

    // Protocolo while com capture
    while (contador.next()) |valor| {
        std.debug.print("{d} ", .{valor}); // 0 1 2 3 4
    }
    std.debug.print("\n", .{});
}

Iteradores da Biblioteca Padrão

const std = @import("std");

pub fn main() void {
    // Split iterator
    var split = std.mem.splitSequence(u8, "Zig,é,incrível", ",");
    while (split.next()) |parte| {
        std.debug.print("'{s}' ", .{parte});
    }
    std.debug.print("\n", .{});

    // Tokenize (ignora delimitadores consecutivos)
    var tokens = std.mem.tokenizeAny(u8, "  Zig  é   incrível  ", " ");
    while (tokens.next()) |token| {
        std.debug.print("'{s}' ", .{token});
    }
    std.debug.print("\n", .{});

    // Window iterator
    const dados = "ABCDEFGH";
    var window = std.mem.window(u8, dados, 3, 1);
    while (window.next()) |w| {
        std.debug.print("{s} ", .{w}); // ABC BCD CDE DEF EFG FGH
    }
    std.debug.print("\n", .{});
}

Iterador Customizado com Estado

const std = @import("std");

const Fibonacci = struct {
    a: u64 = 0,
    b: u64 = 1,
    limite: ?u64 = null,

    pub fn init() Fibonacci {
        return .{};
    }

    pub fn comLimite(limite: u64) Fibonacci {
        return .{ .limite = limite };
    }

    pub fn next(self: *Fibonacci) ?u64 {
        if (self.limite) |lim| {
            if (self.a > lim) return null;
        }
        const valor = self.a;
        const proximo = self.a +| self.b; // saturating add
        self.a = self.b;
        self.b = proximo;
        return valor;
    }
};

pub fn main() void {
    var fib = Fibonacci.comLimite(100);
    while (fib.next()) |n| {
        std.debug.print("{d} ", .{n}); // 0 1 1 2 3 5 8 13 21 34 55 89
    }
    std.debug.print("\n", .{});
}

Composição de Iteradores

const std = @import("std");

fn MapIterator(comptime Src: type, comptime Dst: type) type {
    return struct {
        const Self = @This();
        fonte: *Src,
        transformar: *const fn (std.meta.Elem(Src)) Dst,

        pub fn next(self: *Self) ?Dst {
            const val = self.fonte.next() orelse return null;
            return self.transformar(val);
        }
    };
}

fn FilterIterator(comptime Src: type) type {
    return struct {
        const Self = @This();
        const T = std.meta.Elem(Src);
        fonte: *Src,
        predicado: *const fn (T) bool,

        pub fn next(self: *Self) ?T {
            while (self.fonte.next()) |val| {
                if (self.predicado(val)) return val;
            }
            return null;
        }
    };
}

Iterador sobre Diretório

const std = @import("std");

pub fn main() !void {
    var dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
    defer dir.close();

    var iter = dir.iterate();
    while (try iter.next()) |entrada| {
        const tipo: []const u8 = switch (entrada.kind) {
            .file => "ARQ",
            .directory => "DIR",
            else => "???",
        };
        std.debug.print("[{s}] {s}\n", .{ tipo, entrada.name });
    }
}

Quando Evitar

  • Arrays simples — use for direto: for (array) |item| { ... }
  • Quando todo o conteúdo precisa estar em memória de qualquer forma
  • Operações que precisam de acesso aleatório (índice)

Veja Também

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.