---
title: "Cheatsheet: Iterator em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-iterator-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-iterator-em-zig.MD"
description: "Design pattern Iterator implementado em Zig: iteradores customizados, protocolo next(), composição de iteradores e padrões idiomáticos. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Iterator em Zig

Design pattern Iterator implementado em Zig: iteradores customizados, protocolo next(), composição de iteradores e padrões idiomáticos. Guia completo em português.


# 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

```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

```zig
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

```zig
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

```zig
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

```zig
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 });
    }
}
```

## Considerações de Performance

- **Iteradores são lazy por natureza**: o valor é calculado (ou lido) apenas quando `next()` é chamado. Isso é excelente para sequências grandes — você processa um elemento de cada vez sem alocar um buffer intermediário com todos os elementos.
- **Zero alocação**: iteradores customizados em Zig tipicamente vivem na stack. Não há overhead de heap, não há GC pause. `Fibonacci`, `Contador`, `FilterIterator` — todos são structs de poucos bytes na stack.
- **Iteradores compostos com comptime**: `MapIterator` e `FilterIterator` parametrizados por comptime tipos permitem ao compilador inlinar a função de transformação/predicado. O resultado é equivalente a escrever o loop manualmente.
- **Atenção ao `hasNext` vs `next`**: o protocolo Zig usa apenas `next() -> ?T`. Não implemente `hasNext` separado — isso exige que o iterador "espreite" o próximo valor, o que pode ser caro para iteradores de I/O.

## Iterador com Reset e Peek

Para casos onde você precisa olhar o próximo elemento sem avançar:

```zig
const PeekableContador = struct {
    atual: u32,
    maximo: u32,
    _peeked: ?u32 = null,

    pub fn peek(self: *PeekableContador) ?u32 {
        if (self._peeked == null) {
            self._peeked = self.next();
        }
        return self._peeked;
    }

    pub fn next(self: *PeekableContador) ?u32 {
        if (self._peeked) |val| {
            self._peeked = null;
            return val;
        }
        if (self.atual >= self.maximo) return null;
        const val = self.atual;
        self.atual += 1;
        return val;
    }
};
```

O `peek` é útil em parsers onde você precisa decidir qual caminho seguir com base no próximo token sem consumi-lo.

## Erros Comuns

**Modificar a coleção durante a iteração**: se você remove ou insere elementos em um `ArrayList` enquanto itera sobre ele com um iterador baseado em índice, o índice fica desincronizado. Prefira coletar os índices a remover e limpar após a iteração.

**Iterador com referência à coleção que muda de endereço**: se o iterador guarda um ponteiro para um `ArrayList` e o `ArrayList` é realocado (por `append`), o ponteiro fica dangling. Sempre passe o `ArrayList` como argumento para o iterador depois de todas as inserções, ou use `ensureTotalCapacity` antes de criar o iterador.

**Retornar `[]T` em vez de `?T` em `next`**: retornar um slice vazio como sentinela de fim (`len == 0`) é ambíguo — o iterador pode legitimamente retornar um slice vazio que representa um campo vazio em um CSV, por exemplo. Use sempre `?T` onde `null` significa explicitamente "iteração encerrada".

## Perguntas Frequentes

**Como criar um iterador que itera de trás para frente?**
Implemente um `ReverseIterator` que guarda o índice atual iniciando em `len` e decrementa: `if (self.idx == 0) return null; self.idx -= 1; return self.dados[self.idx];`. O protocolo `next() -> ?T` funciona igualmente.

**Posso usar iteradores Zig com `for`?**
O `for` nativo de Zig (`for (slice) |item|`) funciona apenas com arrays, slices e ranges. Para iteradores customizados, use `while (iter.next()) |val| { ... }`. Não existe (ainda) sintaxe especial para iteradores customizados no `for`.

**Como colecionar os resultados de um iterador em um ArrayList?**
Use um loop com `try lista.append(iter.next() orelse break)`, ou escreva uma função auxiliar: `fn coletarIterador(iter: anytype, allocator: Allocator) !ArrayList(...)`.

## 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

- [Pipeline](/padroes/pipeline/) — Composição de estágios de processamento
- [Arrays e Slices](/cheatsheets/arrays-slices/) — Iteração sobre arrays nativos
- [Strings](/cheatsheets/strings/) — Iteradores de string e Unicode
- [Operações de I/O](/cheatsheets/io-operacoes/) — Readers como iteradores
- [Biblioteca Padrão](/cheatsheets/std-lib-essencial/) — Iteradores da std
