Perguntas de Entrevista sobre Comptime em Zig
Comptime (compile-time execution) é o recurso mais distintivo de Zig — é o que substitui macros, templates, generics e preprocessador em uma única abstração elegante. Entrevistadores adoram perguntas sobre comptime porque revelam compreensão profunda da linguagem e capacidade de pensar sobre dois momentos de execução distintos (compilação e runtime).
Conceitos Fundamentais
O que é comptime e como difere de macros?
Comptime é a capacidade de executar código Zig em tempo de compilação usando a mesma linguagem e semântica do runtime. Diferente de macros (C/C++) que operam no nível textual ou templates (C++) que são uma sublinguagem, comptime é simplesmente Zig executando em tempo de compilação.
const tamanho = blk: {
var s: usize = 0;
for ("hello") |_| s += 1;
break :blk s;
};
// tamanho é 5, calculado em compilação
Diferenças de macros:
- Macros operam em texto; comptime opera em valores e tipos
- Macros não têm type-checking; comptime é totalmente type-safe
- Macros são difíceis de debugar; comptime usa o mesmo debugger
- Macros podem gerar código inválido; comptime garante código válido
Como usar comptime em parâmetros de função?
Parâmetros marcados como comptime devem ser conhecidos em tempo de compilação, permitindo que a função tome decisões baseadas neles:
fn criarArray(comptime T: type, comptime tamanho: usize) [tamanho]T {
var arr: [tamanho]T = undefined;
for (&arr) |*elem| {
elem.* = 0;
}
return arr;
}
const arr = criarArray(u32, 10); // [10]u32 preenchido com zeros
Explique generic programming em Zig via comptime.
Zig implementa generics usando comptime para parâmetros de tipo:
fn ArrayList(comptime T: type) type {
return struct {
items: []T,
capacity: usize,
allocator: std.mem.Allocator,
const Self = @This();
pub fn init(allocator: std.mem.Allocator) Self {
return .{
.items = &[_]T{},
.capacity = 0,
.allocator = allocator,
};
}
pub fn append(self: *Self, item: T) !void {
// ...implementação...
}
};
}
Cada instanciação (ArrayList(u32), ArrayList([]const u8)) gera um tipo concreto distinto em tempo de compilação.
Perguntas Intermediárias
O que é @TypeOf e @typeInfo e como usá-los?
@TypeOf retorna o tipo de uma expressão. @typeInfo retorna informação estruturada sobre um tipo:
fn imprimirCampos(comptime T: type) void {
const info = @typeInfo(T);
switch (info) {
.@"struct" => |s| {
inline for (s.fields) |field| {
std.debug.print("Campo: {s}, Tipo: {}\n", .{ field.name, field.type });
}
},
else => @compileError("Tipo não suportado"),
}
}
Isso permite introspecção de tipos em tempo de compilação — serialização, debugging, geração de código.
Como usar inline for e inline while?
Loops inline são desdobrados em tempo de compilação:
fn somaFields(comptime T: type, valor: T) i64 {
var soma: i64 = 0;
const fields = @typeInfo(T).@"struct".fields;
inline for (fields) |field| {
if (@typeInfo(field.type) == .int) {
soma += @as(i64, @field(valor, field.name));
}
}
return soma;
}
O loop é completamente desdobrado em compilação — em runtime, é código linear sem branches.
Como comptime se relaciona com o build system?
O build system de Zig (build.zig) é um programa Zig executado em comptime, permitindo lógica arbitrária para configuração de build:
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const exe = b.addExecutable(.{
.name = "meu-app",
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
b.installArtifact(exe);
}
Perguntas Avançadas
Como implementar um serializer genérico usando comptime?
fn serialize(comptime T: type, valor: T, writer: anytype) !void {
const info = @typeInfo(T);
switch (info) {
.int => try writer.writeInt(T, valor, .little),
.@"struct" => |s| {
inline for (s.fields) |field| {
try serialize(field.type, @field(valor, field.name), writer);
}
},
.pointer => |p| {
if (p.size == .Slice) {
try writer.writeInt(u32, @intCast(valor.len), .little);
for (valor) |item| {
try serialize(p.child, item, writer);
}
}
},
else => @compileError("Tipo não suportável: " ++ @typeName(T)),
}
}
Quais são as limitações de comptime?
- Não pode fazer IO (ler arquivos, networking) durante compilação (exceto
@embedFile) - Não pode alocar memória dinâmica ilimitada (há limites de memória do compilador)
- Loops devem ter número de iterações determinável em compilação
- Tempo de compilação é limitado
- Side effects são limitados (não pode modificar estado global persistente)
Quando NÃO usar comptime?
- Quando o valor realmente só é conhecido em runtime
- Quando comptime aumenta significativamente o tempo de compilação
- Quando a metaprogramação torna o código ilegível
- Para lógica que muda frequentemente (compile-time = recompilação necessária)
Preparação
Comptime é um tópico avançado. Certifique-se de dominar os fundamentos e memória antes de se aprofundar. Pratique com desafios de código e explore exemplos no ecossistema. Veja tutoriais para exercícios práticos de comptime.