Perguntas Básicas de Entrevista sobre Zig

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.
  • usize e isize: Tamanho de ponteiro da plataforma.
  • comptime_int e comptime_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: *T para ponteiros mutáveis, *const T para ponteiros constantes. Use quando precisa modificar o valor original.
  • Slices: []T e []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:

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.

Continue aprendendo Zig

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