---
title: "Comptime em Zig: O Poder da Execução em Tempo de Compilação"
url: "https://ziglang.com.br/tutoriais/comptime-em-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/comptime-em-zig.MD"
description: "Tutorial completo sobre comptime em Zig. Aprenda metaprogramação, generics, reflexão de tipos, geração de código em tempo de compilação e como comptime substitui macros C/C++ com segurança e expressividade."
date: "2026-02-08"
author: ""
---

# Comptime em Zig: O Poder da Execução em Tempo de Compilação

Tutorial completo sobre comptime em Zig. Aprenda metaprogramação, generics, reflexão de tipos, geração de código em tempo de compilação e como comptime substitui macros C/C++ com segurança e expressividade.


Se existe **uma feature** que define o que torna Zig especial, é o `comptime`. Enquanto linguagens como C dependem de um preprocessador baseado em substituição de texto, e C++ usa templates com sintaxe críptica, Zig permite que você execute **código real da linguagem** em tempo de compilação — com a mesma sintaxe, as mesmas regras e a mesma depurabilidade do código que roda em tempo de execução.

Neste tutorial, vamos explorar `comptime` em profundidade: desde os conceitos básicos até técnicas avançadas de metaprogramação que farão você repensar o que é possível em uma linguagem de sistemas. Se você ainda está começando com Zig, recomendamos ler a [Introdução ao Zig](/tutoriais/introducao-ao-zig/) antes de continuar.

## O que é Comptime?

`comptime` é uma palavra-chave do Zig que instrui o compilador a **avaliar expressões durante a compilação** em vez de em tempo de execução. Mas vai muito além de simples constantes — em Zig, você pode executar funções inteiras, iterar sobre arrays, manipular tipos e até gerar structs completas, tudo durante a compilação.

### A Ideia Central

A filosofia do comptime é: **não crie uma linguagem separada para metaprogramação**. Em vez disso, use a mesma linguagem que você já conhece.

```zig
const std = @import("std");

fn fibonacci(n: u16) u16 {
    if (n == 0 or n == 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

pub fn main() void {
    // Calculado em TEMPO DE COMPILAÇÃO
    // O binário já contém o valor 55 — zero custo em runtime
    const fib10 = comptime fibonacci(10);

    std.debug.print("fibonacci(10) = {}\n", .{fib10});
}
```

Observe: `fibonacci` é uma **função normal**. Não existe nenhuma anotação especial que a torna "compatível com comptime". O compilador simplesmente executa a função durante a compilação quando você usa a palavra-chave `comptime`.

### O que Comptime Pode Fazer

| Capacidade | Exemplo |
|---|---|
| Calcular valores constantes | `comptime fibonacci(10)` |
| Definir tamanhos de arrays | `var arr: [comptime tamanho()]u8 = undefined;` |
| Criar tipos genéricos | `fn ArrayList(comptime T: type) type { ... }` |
| Reflexão sobre tipos | `@typeInfo(T)` para inspecionar structs, enums, etc. |
| Gerar código condicionalmente | `if (comptime builtin.os.tag == .linux) { ... }` |
| Validar entradas em compilação | `@compileError("mensagem")` |
| Desdobrar loops | `inline for` sobre tuplas conhecidas em comptime |

### O que Comptime NÃO Pode Fazer

Para manter a linguagem previsível, `comptime` tem restrições intencionais:

- **Não pode fazer I/O**: sem acesso a arquivos, rede ou stdin/stdout durante compilação.
- **Não pode alocar memória em heap**: sem `malloc` ou allocators em comptime.
- **Não depende da arquitetura do host**: o código comptime não sabe em qual máquina está compilando (isso garante builds reproduzíveis).
- **Não pode chamar funções externas C** (como `printf`) em comptime.

## Comptime vs Macros C/C++

Programadores vindos de C frequentemente perguntam: "Comptime substitui macros?". A resposta é sim — e é **imensamente superior**. Para uma [comparação completa entre Zig e C](/tutoriais/zig-vs-c-comparacao/), veja nosso tutorial dedicado.

### Comparação Direta

**Macro C — MAX genérico:**
```c
// Problemas:
// 1. Sem verificação de tipos
// 2. Efeitos colaterais (avaliação dupla de argumentos)
// 3. Impossível depurar com gdb/lldb
// 4. Mensagens de erro incompreensíveis

#define MAX(a, b) ((a) > (b) ? (a) : (b))

int x = 5;
int resultado = MAX(x++, 3);
// BUG: x++ avaliado DUAS vezes! resultado imprevisível
```

**Zig — Função comptime genérica:**
```zig
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

pub fn main() void {
    // Seguro, tipado, depurável
    var x: i32 = 5;
    const resultado = max(i32, x, 3);
    // x avaliado UMA vez. Comportamento previsível.

    // Erro de compilação se T não suportar '>'
    // const errado = max([]const u8, "a", "b");
    // ^^^ erro: operador '>' não definido para []const u8
}
```

### Compilação Condicional

**C:**
```c
#ifdef DEBUG
    printf("debug: x = %d\n", x);
#endif

#ifdef _WIN32
    #include <windows.h>
#elif __linux__
    #include <unistd.h>
#endif
```

**Zig:**
```zig
const builtin = @import("builtin");
const std = @import("std");

pub fn main() void {
    // Compilação condicional — mas é código REAL, não texto
    if (builtin.mode == .Debug) {
        std.debug.print("debug: x = {}\n", .{x});
    }
    // O compilador elimina esse bloco em ReleaseFast

    // Detecção de plataforma
    const os = builtin.os.tag;
    if (os == .windows) {
        // Código Windows
    } else if (os == .linux) {
        // Código Linux
    }
}
```

### Tabela Comparativa

| Aspecto | Macros C/C++ | Templates C++ | Comptime Zig |
|---|---|---|---|
| **Linguagem** | Substituição de texto | Meta-linguagem separada | Mesma linguagem |
| **Verificação de tipos** | ❌ Nenhuma | ✅ Parcial | ✅ Completa |
| **Depuração** | ❌ Impossível | ❌ Muito difícil | ✅ Normal |
| **Mensagens de erro** | ❌ Crípticas | ❌ Páginas de erros | ✅ Claras e localizadas |
| **Efeitos colaterais** | ❌ Possíveis | ✅ Sem | ✅ Sem |
| **Recursão** | ❌ Limitada | ✅ Sim | ✅ Sim |
| **Geração de tipos** | ❌ Não | ✅ Sim | ✅ Sim |

## Variáveis e Blocos Comptime

### Variáveis comptime

Uma variável `comptime` existe apenas durante a compilação e **nunca aparece no binário final**:

```zig
pub fn main() void {
    // Variável comptime — calculada durante compilação
    comptime var x: i32 = 0;
    x += 1;
    x += 1;
    x += 1;
    // x agora vale 3, mas esse cálculo aconteceu no compilador

    // Atribuir a uma constante runtime
    const resultado: i32 = x; // No binário, isso é simplesmente "3"

    std.debug.print("resultado = {}\n", .{resultado});
}
```

### Blocos comptime

Você pode marcar um bloco inteiro como comptime para executar lógica complexa durante a compilação:

```zig
const std = @import("std");

pub fn main() void {
    // Bloco comptime com resultado
    const mensagem = comptime blk: {
        var buf: [64]u8 = undefined;
        const texto = "ZIG";
        var i: usize = 0;
        for (texto) |c| {
            buf[i] = c;
            i += 1;
            buf[i] = ' ';
            i += 1;
        }
        break :blk buf[0..i];
    };

    std.debug.print("Mensagem: {s}\n", .{mensagem});
    // Saída: "Mensagem: Z I G "
}
```

### comptime_int e comptime_float

Literais numéricos em Zig são de tipos especiais que só existem em comptime:

```zig
const std = @import("std");

pub fn main() void {
    // Literais são comptime_int — precisão arbitrária!
    const grande = 100_000_000_000_000_000_000;
    // ^^^ isso não caberia em u64, mas comptime_int suporta

    // Ao atribuir a um tipo concreto, o compilador verifica o range
    const x: u8 = 42;      // OK: 42 cabe em u8
    // const y: u8 = 256;   // ERRO: 256 não cabe em u8

    // comptime_float é f128 internamente
    const pi = 3.14159265358979323846;
    const pi_f32: f32 = pi; // Conversão com possível perda de precisão
    const pi_f64: f64 = pi; // Mais precisão preservada

    std.debug.print("pi f32 = {d:.15}\n", .{pi_f32});
    std.debug.print("pi f64 = {d:.15}\n", .{pi_f64});
}
```

## Funções com Parâmetros Comptime

Aqui as coisas ficam realmente interessantes. Quando um parâmetro de função é marcado como `comptime`, **o valor deve ser conhecido em tempo de compilação**. Isso é a base dos generics em Zig.

### Generics Básicos

```zig
const std = @import("std");

// Função genérica que funciona com qualquer tipo numérico
fn somar(comptime T: type, a: T, b: T) T {
    return a + b;
}

pub fn main() void {
    // O compilador gera versões especializadas para cada tipo
    const r1 = somar(i32, 10, 20);       // Versão para i32
    const r2 = somar(f64, 3.14, 2.71);   // Versão para f64
    const r3 = somar(u8, 100, 50);       // Versão para u8

    std.debug.print("i32: {}, f64: {d:.2}, u8: {}\n", .{ r1, r2, r3 });
}
```

### Funções que Retornam Tipos

Em Zig, **tipos são valores de primeira classe** em comptime. Uma função pode receber e retornar tipos, incluindo [structs, enums e unions](/tutoriais/structs-enums-unions-zig/):

```zig
const std = @import("std");

// Função que cria um tipo de array com tamanho customizado
fn Array(comptime T: type, comptime tamanho: comptime_int) type {
    return [tamanho]T;
}

// Função que cria um tipo de par (tupla tipada)
fn Par(comptime A: type, comptime B: type) type {
    return struct {
        primeiro: A,
        segundo: B,

        const Self = @This();

        pub fn criar(a: A, b: B) Self {
            return .{ .primeiro = a, .segundo = b };
        }

        pub fn trocar(self: Self) Par(B, A) {
            return Par(B, A).criar(self.segundo, self.primeiro);
        }
    };
}

pub fn main() void {
    // Tipo Array de 5 inteiros
    const MeuArray = Array(i32, 5);
    var arr: MeuArray = .{ 1, 2, 3, 4, 5 };
    arr[0] = 10;

    // Tipo Par de string e inteiro
    const par = Par([]const u8, i32).criar("idade", 30);
    std.debug.print("{s} = {}\n", .{ par.primeiro, par.segundo });

    // Trocar os elementos do par
    const invertido = par.trocar();
    std.debug.print("{} = {s}\n", .{ invertido.primeiro, invertido.segundo });
}
```

### anytype — Inferência em Comptime

Quando um parâmetro é declarado como `anytype`, o compilador infere o tipo automaticamente. Isso funciona como "templates" implícitos:

```zig
const std = @import("std");

// anytype permite aceitar qualquer tipo
fn dobrar(valor: anytype) @TypeOf(valor) {
    return valor + valor;
}

fn imprimir(valor: anytype) void {
    const T = @TypeOf(valor);
    switch (@typeInfo(T)) {
        .int, .comptime_int => std.debug.print("Inteiro: {}\n", .{valor}),
        .float, .comptime_float => std.debug.print("Float: {d:.4}\n", .{valor}),
        .pointer => |ptr_info| {
            if (ptr_info.size == .Slice and ptr_info.child == u8) {
                std.debug.print("String: {s}\n", .{valor});
            } else {
                std.debug.print("Ponteiro: {*}\n", .{valor});
            }
        },
        else => std.debug.print("Tipo: {}\n", .{@typeName(T)}),
    }
}

pub fn main() void {
    std.debug.print("{}\n", .{dobrar(@as(i32, 21))}); // 42
    std.debug.print("{d}\n", .{dobrar(@as(f64, 1.5))}); // 3.0

    imprimir(@as(i32, 42));
    imprimir(@as(f64, 3.14));
    imprimir("Olá Zig!");
}
```

## Tipos como Valores de Primeira Classe

Em Zig, `type` é um tipo real que pode ser armazenado em variáveis, passado para funções e retornado — tudo em comptime. Isso é **radicalmente diferente** de qualquer outra linguagem de sistemas.

### Manipulando Tipos

```zig
const std = @import("std");

pub fn main() void {
    // Tipos podem ser armazenados em constantes
    const MeuTipo = i32;
    var x: MeuTipo = 42;
    x += 1;

    // Tipos podem ser escolhidos condicionalmente
    const Numero = if (@sizeOf(usize) >= 8) i64 else i32;
    const valor: Numero = 1000;

    std.debug.print("Tamanho de Numero: {} bytes\n", .{@sizeOf(Numero)});
    std.debug.print("Valor: {}\n", .{valor});

    // Branching sobre tipos em comptime
    const tipo_escolhido = comptime blk: {
        const arquitetura = @import("builtin").cpu.arch;
        break :blk switch (arquitetura) {
            .x86_64 => f64,
            .aarch64 => f64,
            else => f32,
        };
    };

    std.debug.print("Tipo de float para esta arquitetura: {}\n", .{@typeName(tipo_escolhido)});
}
```

### Criando Tipos Dinamicamente com @Type

A builtin `@Type` permite **construir tipos programaticamente** a partir de uma descrição:

```zig
const std = @import("std");

// Criar um tipo inteiro com N+1 bits
fn InteiroMaior(comptime T: type) type {
    const info = @typeInfo(T).int;
    return @Type(.{
        .int = .{
            .bits = info.bits + 1,
            .signedness = info.signedness,
        },
    });
}

// Criar um tipo inteiro sem sinal a partir de um com sinal
fn SemSinal(comptime T: type) type {
    const info = @typeInfo(T).int;
    return @Type(.{
        .int = .{
            .bits = info.bits,
            .signedness = .unsigned,
        },
    });
}

test "manipulação de tipos" {
    // u8 → u9
    try std.testing.expect(InteiroMaior(u8) == u9);

    // i32 → i33
    try std.testing.expect(InteiroMaior(i32) == i33);

    // i64 → u64
    try std.testing.expect(SemSinal(i64) == u64);

    // i16 → u16
    try std.testing.expect(SemSinal(i16) == u16);
}
```

## Geração de Código em Comptime

Uma das aplicações mais poderosas de comptime é a geração de dados e código durante a compilação — sem nenhum custo em tempo de execução.

### Lookup Tables

```zig
const std = @import("std");

// Gerar tabela de quadrados em comptime
fn gerarTabelaQuadrados(comptime tamanho: usize) [tamanho]u64 {
    var tabela: [tamanho]u64 = undefined;
    for (0..tamanho) |i| {
        tabela[i] = i * i;
    }
    return tabela;
}

// Gerar tabela de senos pré-calculados (para games/DSP)
fn gerarTabelaSenos(comptime pontos: usize) [pontos]f64 {
    var tabela: [pontos]f64 = undefined;
    const passo = 2.0 * std.math.pi / @as(f64, @floatFromInt(pontos));
    for (0..pontos) |i| {
        tabela[i] = @sin(passo * @as(f64, @floatFromInt(i)));
    }
    return tabela;
}

// CRC32 lookup table — usada em checksum de dados
fn gerarCRC32Table() [256]u32 {
    var tabela: [256]u32 = undefined;
    for (0..256) |i| {
        var crc: u32 = @intCast(i);
        for (0..8) |_| {
            if (crc & 1 == 1) {
                crc = (crc >> 1) ^ 0xEDB88320;
            } else {
                crc = crc >> 1;
            }
        }
        tabela[i] = crc;
    }
    return tabela;
}

// Todas estas tabelas são calculadas em TEMPO DE COMPILAÇÃO
// e incorporadas ao binário como dados estáticos
const QUADRADOS = comptime gerarTabelaQuadrados(100);
const SENOS = comptime gerarTabelaSenos(360);
const CRC32_TABLE = comptime gerarCRC32Table();

pub fn main() void {
    std.debug.print("7² = {}\n", .{QUADRADOS[7]}); // 49
    std.debug.print("sin(90°) ≈ {d:.6}\n", .{SENOS[90]}); // ~1.0
    std.debug.print("CRC32[0] = 0x{X:0>8}\n", .{CRC32_TABLE[0]}); // 0x00000000
    std.debug.print("CRC32[1] = 0x{X:0>8}\n", .{CRC32_TABLE[1]}); // 0x77073096
}
```

> 💡 **Por que isso importa?** Em C, para gerar essas tabelas em tempo de compilação, você precisaria de scripts Python/Perl gerando código C, ou macros absurdamente complexas. Em Zig, é apenas uma função normal com a palavra-chave `comptime`.

### Implementações Condicionais

Use comptime para selecionar a melhor implementação com base nas características da plataforma (veja também [cross-compilation em Zig](/tutoriais/zig-cross-compilation/)):

```zig
const std = @import("std");
const builtin = @import("builtin");

fn somaOtimizada(slice: []const i32) i64 {
    // Se a CPU suporta SIMD, use instruções vetoriais
    if (comptime std.Target.x86.featureSetHas(builtin.cpu.features, .avx2)) {
        return somaAVX2(slice);
    } else if (comptime std.Target.x86.featureSetHas(builtin.cpu.features, .sse2)) {
        return somaSSE2(slice);
    } else {
        return somaEscalar(slice);
    }
}

fn somaEscalar(slice: []const i32) i64 {
    var total: i64 = 0;
    for (slice) |valor| {
        total += valor;
    }
    return total;
}

fn somaSSE2(slice: []const i32) i64 {
    // Implementação SSE2 aqui
    return somaEscalar(slice); // fallback simplificado
}

fn somaAVX2(slice: []const i32) i64 {
    // Implementação AVX2 aqui
    return somaEscalar(slice); // fallback simplificado
}
```

O compilador **remove completamente** os branches que não se aplicam à plataforma alvo. O binário final contém apenas a implementação escolhida.

## @typeInfo e Reflexão em Comptime

`@typeInfo` é a ferramenta de reflexão do Zig. Ela retorna uma *tagged union* que descreve completamente qualquer tipo. Combinada com `comptime`, permite criar código que se adapta automaticamente a qualquer tipo de dado.

### Inspecionando Tipos

```zig
const std = @import("std");

fn descreverTipo(comptime T: type) void {
    const info = @typeInfo(T);
    switch (info) {
        .int => |i| {
            const sinal = if (i.signedness == .signed) "com sinal" else "sem sinal";
            @compileLog("Inteiro " ++ sinal, i.bits, "bits");
        },
        .float => |f| {
            @compileLog("Float de", f.bits, "bits");
        },
        .@"struct" => |s| {
            @compileLog("Struct com", s.fields.len, "campos");
            for (s.fields) |campo| {
                @compileLog("  campo:", campo.name);
            }
        },
        .pointer => |p| {
            @compileLog("Ponteiro para", @typeName(p.child));
        },
        else => @compileLog("Outro tipo:", @typeName(T)),
    }
}

const Pessoa = struct {
    nome: []const u8,
    idade: u32,
    ativo: bool,
};

// Chamado em comptime — imprime informações no log de compilação
comptime {
    descreverTipo(i32);       // "Inteiro com sinal, 32 bits"
    descreverTipo(f64);       // "Float de 64 bits"
    descreverTipo(Pessoa);    // "Struct com 3 campos"
    descreverTipo(*const u8); // "Ponteiro para u8"
}
```

### Iterando sobre Campos de uma Struct

Este é um padrão extremamente útil — percorrer todos os campos de uma [struct](/tutoriais/structs-enums-unions-zig/) em comptime:

```zig
const std = @import("std");

const Configuracao = struct {
    host: []const u8 = "localhost",
    porta: u16 = 8080,
    max_conexoes: u32 = 100,
    debug: bool = false,
    timeout_ms: u64 = 5000,
};

fn imprimirCampos(comptime T: type, valor: T) void {
    const info = @typeInfo(T).@"struct";
    std.debug.print("=== {} ===\n", .{@typeName(T)});
    inline for (info.fields) |campo| {
        const v = @field(valor, campo.name);
        std.debug.print("  {s}: {any}\n", .{ campo.name, v });
    }
}

pub fn main() void {
    const config = Configuracao{
        .host = "meuservidor.com",
        .porta = 3000,
    };
    imprimirCampos(Configuracao, config);
}
```

Saída:
```
=== Configuracao ===
  host: meuservidor.com
  porta: 3000
  max_conexoes: 100
  debug: false
  timeout_ms: 5000
```

### Serialização Automática para JSON

Combinando reflexão com comptime, podemos criar um serializador genérico:

```zig
const std = @import("std");

fn paraJSON(comptime T: type, valor: T, writer: anytype) !void {
    const info = @typeInfo(T);
    switch (info) {
        .@"struct" => |s| {
            try writer.writeAll("{");
            var primeiro = true;
            inline for (s.fields) |campo| {
                if (!primeiro) try writer.writeAll(",");
                primeiro = false;
                try writer.print("\"{s}\":", .{campo.name});
                try paraJSON(campo.type, @field(valor, campo.name), writer);
            }
            try writer.writeAll("}");
        },
        .int, .comptime_int => try writer.print("{}", .{valor}),
        .float, .comptime_float => try writer.print("{d}", .{valor}),
        .bool => try writer.print("{}", .{valor}),
        .pointer => |ptr| {
            if (ptr.size == .Slice and ptr.child == u8) {
                try writer.print("\"{s}\"", .{valor});
            }
        },
        .optional => {
            if (valor) |v| {
                try paraJSON(@typeInfo(T).optional.child, v, writer);
            } else {
                try writer.writeAll("null");
            }
        },
        else => try writer.writeAll("null"),
    }
}

const Produto = struct {
    nome: []const u8,
    preco: f64,
    estoque: u32,
    disponivel: bool,
};

pub fn main() !void {
    const produto = Produto{
        .nome = "Teclado Mecânico",
        .preco = 299.90,
        .estoque = 42,
        .disponivel = true,
    };

    const stdout = std.io.getStdOut().writer();
    try paraJSON(Produto, produto, stdout);
    try stdout.writeAll("\n");
}
```

Saída:
```json
{"nome":"Teclado Mecânico","preco":299.9,"estoque":42,"disponivel":true}
```

O mais impressionante: o compilador **conhece todos os campos em tempo de compilação**, então o código gerado é **tão eficiente quanto escrever a serialização na mão** para cada tipo.

## Exemplos Práticos

### Exemplo 1: ArrayList Genérico

Vamos implementar uma versão simplificada de `ArrayList` — a estrutura de dados mais usada em Zig. Este exemplo também demonstra o uso de [allocators](/tutoriais/gerenciamento-de-memoria-zig/):

```zig
const std = @import("std");
const Allocator = std.mem.Allocator;

fn ArrayList(comptime T: type) type {
    return struct {
        items: []T,
        capacidade: usize,
        tamanho: usize,
        allocator: Allocator,

        const Self = @This();

        pub fn init(allocator: Allocator) Self {
            return .{
                .items = &[_]T{},
                .capacidade = 0,
                .tamanho = 0,
                .allocator = allocator,
            };
        }

        pub fn deinit(self: *Self) void {
            if (self.capacidade > 0) {
                self.allocator.free(self.items.ptr[0..self.capacidade]);
            }
        }

        pub fn append(self: *Self, item: T) !void {
            if (self.tamanho >= self.capacidade) {
                try self.crescer();
            }
            self.items.ptr[self.tamanho] = item;
            self.tamanho += 1;
            self.items.len = self.tamanho;
        }

        pub fn get(self: Self, indice: usize) T {
            if (indice >= self.tamanho) {
                @panic("índice fora dos limites");
            }
            return self.items[indice];
        }

        pub fn slice(self: Self) []const T {
            return self.items.ptr[0..self.tamanho];
        }

        fn crescer(self: *Self) !void {
            const nova_capacidade = if (self.capacidade == 0)
                8
            else
                self.capacidade * 2;

            const novo_buf = try self.allocator.alloc(T, nova_capacidade);

            if (self.tamanho > 0) {
                @memcpy(novo_buf[0..self.tamanho], self.items.ptr[0..self.tamanho]);
            }

            if (self.capacidade > 0) {
                self.allocator.free(self.items.ptr[0..self.capacidade]);
            }

            self.items.ptr = novo_buf.ptr;
            self.items.len = self.tamanho;
            self.capacidade = nova_capacidade;
        }

        // Informações de tipo disponíveis em comptime
        pub const Item = T;
        pub const tamanho_item = @sizeOf(T);
    };
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // ArrayList de inteiros
    var numeros = ArrayList(i32).init(allocator);
    defer numeros.deinit();

    try numeros.append(10);
    try numeros.append(20);
    try numeros.append(30);

    for (numeros.slice()) |n| {
        std.debug.print("{} ", .{n});
    }
    std.debug.print("\n", .{});

    // ArrayList de strings
    var nomes = ArrayList([]const u8).init(allocator);
    defer nomes.deinit();

    try nomes.append("Ana");
    try nomes.append("Bruno");
    try nomes.append("Carlos");

    for (nomes.slice()) |nome| {
        std.debug.print("{s} ", .{nome});
    }
    std.debug.print("\n", .{});

    // Meta-informação disponível em comptime
    std.debug.print("Tamanho de cada item (i32): {} bytes\n", .{ArrayList(i32).tamanho_item});
    std.debug.print("Tamanho de cada item (f64): {} bytes\n", .{ArrayList(f64).tamanho_item});
}
```

### Exemplo 2: Formatador Customizado com comptime

Implementando `format` para suas structs, você integra com todo o sistema de formatação do Zig:

```zig
const std = @import("std");

const Vetor3D = struct {
    x: f64,
    y: f64,
    z: f64,

    const Self = @This();

    // O formato do comptime fmt permite customizar a saída
    pub fn format(
        self: Self,
        comptime fmt: []const u8,
        options: std.fmt.FormatOptions,
        writer: anytype,
    ) !void {
        _ = options;

        if (comptime std.mem.eql(u8, fmt, "coords")) {
            // Formato de coordenadas: (x, y, z)
            try writer.print("({d:.2}, {d:.2}, {d:.2})", .{ self.x, self.y, self.z });
        } else if (comptime std.mem.eql(u8, fmt, "mag")) {
            // Formato de magnitude
            const mag = @sqrt(self.x * self.x + self.y * self.y + self.z * self.z);
            try writer.print("|v| = {d:.4}", .{mag});
        } else if (fmt.len == 0) {
            // Formato padrão
            try writer.print("Vec3({d:.2}, {d:.2}, {d:.2})", .{ self.x, self.y, self.z });
        } else {
            // Formato não reconhecido — erro de COMPILAÇÃO
            std.fmt.invalidFmtError(fmt, self);
        }
    }

    pub fn soma(self: Self, outro: Self) Self {
        return .{
            .x = self.x + outro.x,
            .y = self.y + outro.y,
            .z = self.z + outro.z,
        };
    }
};

pub fn main() void {
    const v = Vetor3D{ .x = 1.0, .y = 2.0, .z = 3.0 };

    std.debug.print("Padrão:     {}\n", .{v});
    std.debug.print("Coordenadas: {coords}\n", .{v});
    std.debug.print("Magnitude:  {mag}\n", .{v});

    // O compilador valida o formato em TEMPO DE COMPILAÇÃO
    // std.debug.print("{invalido}\n", .{v}); // Erro de compilação!
}
```

Saída:
```
Padrão:     Vec3(1.00, 2.00, 3.00)
Coordenadas: (1.00, 2.00, 3.00)
Magnitude:  |v| = 3.7417
```

### Exemplo 3: Gerador de HashMap Perfeito em Comptime

Quando as chaves são conhecidas em tempo de compilação, podemos gerar um mapa otimizado:

```zig
const std = @import("std");

fn MapaEstatico(
    comptime chaves: []const []const u8,
    comptime V: type,
) type {
    return struct {
        valores: [chaves.len]V,

        const Self = @This();

        // Busca O(n) mas com N pequeno e inline, é mais rápido
        // que hash map para poucos elementos
        pub fn get(self: Self, chave: []const u8) ?V {
            inline for (chaves, 0..) |k, i| {
                if (std.mem.eql(u8, k, chave)) {
                    return self.valores[i];
                }
            }
            return null;
        }

        pub fn init(valores: [chaves.len]V) Self {
            return .{ .valores = valores };
        }

        // Validação em comptime: chaves duplicadas
        comptime {
            for (chaves, 0..) |a, i| {
                for (chaves[i + 1 ..]) |b| {
                    if (std.mem.eql(u8, a, b)) {
                        @compileError("Chaves duplicadas no mapa: " ++ a);
                    }
                }
            }
        }
    };
}

pub fn main() void {
    // Mapa de HTTP status codes
    const StatusMap = MapaEstatico(
        &.{ "200", "201", "301", "400", "404", "500" },
        []const u8,
    );

    const status = StatusMap.init(.{
        "OK",
        "Created",
        "Moved Permanently",
        "Bad Request",
        "Not Found",
        "Internal Server Error",
    });

    // Busca é otimizada pelo compilador
    if (status.get("404")) |desc| {
        std.debug.print("404 = {s}\n", .{desc});
    }
    if (status.get("200")) |desc| {
        std.debug.print("200 = {s}\n", .{desc});
    }
    if (status.get("999")) |desc| {
        std.debug.print("999 = {s}\n", .{desc});
    } else {
        std.debug.print("999 = não encontrado\n", .{});
    }
}
```

## inline for e Desdobramento de Loops

O `inline for` é essencial quando você precisa iterar sobre dados conhecidos em comptime enquanto o corpo do loop contém operações runtime:

```zig
const std = @import("std");

// Sistema de validação genérico
fn Validador(comptime T: type) type {
    return struct {
        const Regra = struct {
            nome: []const u8,
            validar: *const fn (T) bool,
        };

        pub fn validarTudo(
            valor: T,
            comptime regras: []const Regra,
        ) !void {
            inline for (regras) |regra| {
                if (!regra.validar(valor)) {
                    std.debug.print("❌ Falhou: {s}\n", .{regra.nome});
                    return error.ValidacaoFalhou;
                }
                std.debug.print("✅ Passou: {s}\n", .{regra.nome});
            }
        }
    };
}

fn ehPositivo(n: i32) bool {
    return n > 0;
}

fn ehPar(n: i32) bool {
    return @mod(n, 2) == 0;
}

fn menorQue100(n: i32) bool {
    return n < 100;
}

pub fn main() !void {
    const V = Validador(i32);
    const regras = [_]V.Regra{
        .{ .nome = "positivo", .validar = &ehPositivo },
        .{ .nome = "par", .validar = &ehPar },
        .{ .nome = "menor que 100", .validar = &menorQue100 },
    };

    std.debug.print("Validando 42:\n", .{});
    try V.validarTudo(42, &regras);

    std.debug.print("\nValidando -5:\n", .{});
    V.validarTudo(-5, &regras) catch {
        std.debug.print("Validação falhou (esperado)\n", .{});
    };
}
```

## Boas Práticas e Armadilhas Comuns

### ✅ Boas Práticas

**1. Use comptime para eliminar overhead de abstração:**
```zig
// BOM: genérico sem custo em runtime
fn comparar(comptime T: type, a: T, b: T) std.math.Order {
    return std.math.order(a, b);
}
```

**2. Valide entradas em comptime com @compileError** (veja também [testes em Zig](/tutoriais/testes-zig/) para validação em runtime)**:**
```zig
fn criarBuffer(comptime tamanho: usize) [tamanho]u8 {
    if (tamanho == 0) {
        @compileError("Buffer não pode ter tamanho zero");
    }
    if (tamanho > 1024 * 1024) {
        @compileError("Buffer na stack não deve exceder 1MB");
    }
    return undefined;
}
```

**3. Use `inline for` apenas quando necessário:**
```zig
// inline for é necessário quando o corpo depende do tipo do campo
fn somarCamposNumericos(comptime T: type, valor: T) f64 {
    var soma: f64 = 0;
    inline for (@typeInfo(T).@"struct".fields) |campo| {
        if (@typeInfo(campo.type) == .int or @typeInfo(campo.type) == .float) {
            soma += @as(f64, @floatFromInt(@field(valor, campo.name)));
        }
    }
    return soma;
}
```

**4. Nomeie funções que retornam tipos com PascalCase:**
```zig
// Convenção do Zig: funções que retornam type usam PascalCase
fn HashMap(comptime K: type, comptime V: type) type { ... }
fn ArrayList(comptime T: type) type { ... }

// Funções normais usam camelCase
fn calcularTotal(items: []const Item) u64 { ... }
```

### ❌ Armadilhas Comuns

**1. Não confunda comptime var com const:**
```zig
pub fn main() void {
    // ATENÇÃO: comptime var é avaliada pelo compilador
    comptime var x: i32 = 0;
    x += 1; // Isso roda no COMPILADOR, não em runtime

    // Para variáveis runtime, use var normalmente
    var y: i32 = 0;
    y += 1; // Isso roda em RUNTIME
}
```

**2. Cuidado com o tamanho do binário:**
```zig
// CUIDADO: inline for com muitos elementos gera muito código
fn processar(dados: [1000]u32) void {
    // Isso gera 1000 cópias do corpo do loop!
    inline for (dados) |d| {
        fazAlgo(d);
    }

    // MELHOR: use for normal quando inline não é necessário
    for (dados) |d| {
        fazAlgo(d);
    }
}
```

**3. Não force comptime desnecessariamente:**
```zig
// RUIM: forçar comptime quando não há benefício
fn somar(a: i32, b: i32) i32 {
    return comptime a + b; // ERRO: a e b não são conhecidos em comptime
}

// BOM: use comptime onde o valor PODE ser conhecido em compilação
fn criarArray(comptime tamanho: usize) [tamanho]u8 {
    return [_]u8{0} ** tamanho;
}
```

**4. Entenda os limites de memória do comptime:**
```zig
// CUIDADO: o compilador tem limites de memória para avaliação comptime
fn gerarGrande() [1_000_000]u64 {
    // Pode ser lento ou falhar durante compilação
    // Tabelas muito grandes devem ser geradas externamente
    // e embedadas com @embedFile
    var tabela: [1_000_000]u64 = undefined;
    for (0..1_000_000) |i| {
        tabela[i] = i * i;
    }
    return tabela;
}
```

### Dica Final: @compileLog para Depuração

`@compileLog` é o `printf` do comptime. Use para inspecionar valores durante a compilação:

```zig
fn meuGenerico(comptime T: type) type {
    // Imprime no terminal DURANTE a compilação
    @compileLog("Criando tipo para:", @typeName(T));
    @compileLog("Tamanho:", @sizeOf(T), "bytes");
    @compileLog("Alinhamento:", @alignOf(T), "bytes");

    return struct {
        dados: T,
    };
}
```

> ⚠️ **Nota:** `@compileLog` causa um erro de compilação proposital — remova antes de fazer o build final. É apenas uma ferramenta de depuração.

## Resumo: O que Você Aprendeu

| Conceito | O que faz | Quando usar |
|---|---|---|
| `comptime` keyword | Força avaliação em tempo de compilação | Cálculos de constantes, tamanhos de array |
| Parâmetros `comptime` | Permite generics | Funções que operam sobre tipos |
| `type` como valor | Tipos são valores de primeira classe | Criar estruturas de dados genéricas |
| `@typeInfo` | Reflexão sobre tipos | Serialização, validação, geração de código |
| `@Type` | Criar tipos programaticamente | Manipulação avançada de tipos |
| `inline for` | Desdobra loops em comptime | Iterar sobre campos de structs |
| `@compileError` | Gerar erros em compilação | Validar parâmetros comptime |
| `@compileLog` | Debug print em compilação | Depuração de código comptime |

## Próximos Passos

Agora que você domina `comptime`, está pronto para ir mais fundo:

1. 📖 **Está começando com Zig?** — Comece pelo [Guia de Instalação](/tutoriais/como-instalar-zig/) para configurar seu ambiente.
2. 🔄 **Vem do C?** — Veja como comptime substitui macros no nosso [Guia de Migração C para Zig](/tutoriais/zig-para-programadores-c/).
3. 📚 **Documentação oficial** — A seção sobre [comptime no Language Reference](https://ziglang.org/documentation/master/#comptime) é a referência canônica.
4. 🧪 **Pratique** — Reescreva uma macro C complexa que você usa como uma função comptime em Zig.
5. 🏗️ **[Zig Build System](/tutoriais/zig-build-system/)** — Veja como comptime é usado no sistema de build do Zig.
6. 🔍 **Estude a std** — O código-fonte da [biblioteca padrão do Zig](https://github.com/ziglang/zig/tree/master/lib/std) é um masterclass em uso de comptime.

---

*Este tutorial é parte da série avançada do ZigLang Brasil. Se comptime mudou sua forma de pensar sobre metaprogramação, compartilhe com outros desenvolvedores!*
