Perguntas Básicas de Entrevista sobre Zig
Estas perguntas cobrem os fundamentos da linguagem Zig e são o ponto de partida para qualquer entrevista técnica. Mesmo para posições seniores, recrutadores frequentemente começam com perguntas básicas para verificar que o candidato tem a base sólida. Domine cada uma dessas questões antes de avançar para temas mais específicos como memória ou concorrência.
Sobre a Linguagem
O que é Zig e quais são seus principais objetivos de design?
Zig é uma linguagem de programação de sistemas de uso geral, projetada para ser uma alternativa moderna a C. Seus principais objetivos incluem:
- Sem comportamento oculto: Diferente de C++, Zig não tem chamadas de função implícitas, operadores ocultos ou alocações de memória escondidas.
- Segurança de memória sem GC: Detecção de erros em tempo de compilação e runtime (em modo debug), sem garbage collector.
- Legibilidade e manutenibilidade: Código Zig deve ser fácil de ler e manter.
- Interoperabilidade com C: Capacidade de importar headers C e chamar funções C sem overhead.
- Comptime: Execução de código em tempo de compilação para metaprogramação sem macros.
Quais as diferenças entre var e const?
const declara uma variável imutável — seu valor não pode ser alterado após a inicialização. var declara uma variável mutável. Em Zig, a preferência é sempre por const a menos que mutabilidade seja necessária.
const x: u32 = 42; // imutável
var y: u32 = 42; // mutável
y += 1; // OK
// x += 1; // erro de compilação
Explique o sistema de tipos numéricos de Zig.
Zig oferece tipos numéricos explícitos com tamanho definido:
- Inteiros:
i8,i16,i32,i64,i128,u8,u16,u32,u64,u128— inteiros com sinal e sem sinal. - Ponto flutuante:
f16,f32,f64,f80,f128. - Tamanhos arbitrários:
i3,u7, etc. — inteiros de qualquer tamanho de bit. usizeeisize: Tamanho de ponteiro da plataforma.comptime_intecomptime_float: Tipos numéricos de tempo de compilação com precisão arbitrária.
Não há conversões implícitas entre tipos numéricos — todas as conversões devem ser explícitas com @intCast, @floatCast, etc.
Tipos Compostos
O que é uma tagged union e quando usá-la?
Uma tagged union (union com tag) combina uma union com um enum que indica qual campo está ativo. É o mecanismo de Zig para representar valores que podem ser de um de vários tipos:
const Token = union(enum) {
number: f64,
string: []const u8,
eof,
pub fn isEnd(self: Token) bool {
return self == .eof;
}
};
Use tagged unions para: parsers (diferentes tipos de tokens), protocolos de rede (diferentes tipos de mensagens), ASTs (diferentes tipos de nós).
Qual a diferença entre slices e arrays em Zig?
Arrays têm tamanho fixo, conhecido em tempo de compilação:
const arr: [5]u8 = .{ 1, 2, 3, 4, 5 };
Slices são uma visão sobre dados contíguos, com tamanho conhecido em runtime:
const slice: []const u8 = arr[1..4]; // {2, 3, 4}
Um slice é internamente um par de ponteiro e comprimento. Arrays vivem na stack; slices podem referenciar dados na stack ou heap.
Explique Optionals em Zig (?T).
Um optional representa um valor que pode ou não estar presente — a alternativa segura de Zig a null pointers:
var maybe_num: ?u32 = 42;
maybe_num = null;
if (maybe_num) |num| {
// num é u32, garantido não-nulo
std.debug.print("{}\n", .{num});
}
// Ou com orelse para valor default
const val = maybe_num orelse 0;
Optionals forçam o programador a tratar explicitamente o caso nulo, eliminando null pointer dereferences.
Controle de Fluxo
Como o for loop funciona em Zig?
O for de Zig itera sobre slices e ranges, e pode capturar tanto o elemento quanto o índice:
const items = [_]u8{ 'a', 'b', 'c' };
// Iteração simples
for (items) |item| {
std.debug.print("{c}\n", .{item});
}
// Com índice
for (items, 0..) |item, i| {
std.debug.print("[{}] = {c}\n", .{i, item});
}
// Iteração sobre múltiplos slices
for (items, other_items) |a, b| {
// ...
}
O que é defer e errdefer?
defer executa uma expressão ao sair do bloco atual, independente de como a saída acontece. errdefer executa apenas quando o bloco sai com um erro:
fn abrirArquivo(path: []const u8) !File {
const file = try std.fs.cwd().openFile(path, .{});
errdefer file.close(); // fecha apenas se retornar erro
// ... operações que podem falhar
return file;
}
defer é fundamental para garantir cleanup de recursos — equivalente ao RAII de C++ mas explícito e sem mágica.
Funções
Explique os tipos de parâmetros de função em Zig.
Zig tem diferentes convenções de passagem de parâmetros:
- Por valor (padrão): O valor é copiado para a função. Para structs grandes, o compilador pode otimizar para passagem por referência implicitamente.
- Ponteiros:
*Tpara ponteiros mutáveis,*const Tpara ponteiros constantes. Use quando precisa modificar o valor original. - Slices:
[]Te[]const T— a forma idiomática de passar coleções.
fn processar(dados: []const u8) void { ... } // slice constante
fn modificar(dados: *[5]u8) void { ... } // ponteiro para array
fn calcular(valor: u32) u32 { return valor * 2; } // por valor
O que são function pointers e quando usá-los?
Function pointers permitem passar funções como valores, essencial para callbacks e polimorfismo em tempo de execução:
const Comparator = fn (a: i32, b: i32) bool;
fn ordenar(items: []i32, lessThan: Comparator) void {
// usa lessThan para comparar elementos
}
fn crescente(a: i32, b: i32) bool { return a < b; }
fn decrescente(a: i32, b: i32) bool { return a > b; }
Como se Preparar
Estas perguntas cobrem os fundamentos. Para avançar, estude:
- Memória e allocators — o tema mais importante
- Error handling — conceito central
- Comptime — diferencial de Zig
- Desafios de código — pratique implementação
Complemente sua preparação com tutoriais práticos e construa projetos para seu portfólio. Siga o roadmap do desenvolvedor Zig para um plano de estudo estruturado.