Perguntas de Entrevista sobre Design Patterns em Zig

Perguntas de Entrevista sobre Design Patterns em Zig

Design patterns em Zig diferem significativamente dos padrões clássicos de OOP. Zig favorece composição sobre herança, explicitação sobre magia, e simplicidade sobre sofisticação. Entrevistadores de posições seniores avaliam sua capacidade de projetar APIs e arquiteturas idiomáticas em Zig.

Padrões Idiomáticos de Zig

Explique o Allocator Pattern.

O padrão mais fundamental de Zig: funções que alocam memória recebem um Allocator como parâmetro, tornando alocação explícita e testável. Veja perguntas de memória para detalhes aprofundados.

const Parser = struct {
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) Parser {
        return .{ .allocator = allocator };
    }

    pub fn parse(self: *Parser, input: []const u8) !Ast {
        const nodes = try self.allocator.alloc(Node, 100);
        errdefer self.allocator.free(nodes);
        // ...
    }
};

Como Zig implementa interfaces/polimorfismo?

Zig não tem interfaces ou classes abstratas. O polimorfismo é alcançado via:

1. Comptime duck typing (mais idiomático):

fn processar(writer: anytype) !void {
    try writer.writeAll("dados");
}
// Aceita qualquer tipo que tenha .writeAll

2. Fat pointers (vtable manual):

const Writer = struct {
    ptr: *anyopaque,
    writeFn: *const fn (*anyopaque, []const u8) error{...}!usize,

    pub fn write(self: Writer, data: []const u8) !usize {
        return self.writeFn(self.ptr, data);
    }
};

A biblioteca padrão usa esse padrão extensivamente (ex: std.mem.Allocator).

Explique o padrão Init/Deinit.

O equivalente de Zig ao RAII de C++ — explícito e sem magia:

const Recurso = struct {
    dados: []u8,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) !Recurso {
        return .{
            .dados = try allocator.alloc(u8, 1024),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Recurso) void {
        self.allocator.free(self.dados);
    }
};

// Uso:
var r = try Recurso.init(allocator);
defer r.deinit();

O que é o padrão “return error or value” (Builder pattern para erros)?

fn configurar() !Config {
    var config = Config{};

    config.host = try resolverHost();
    errdefer config.host.deinit();

    config.porta = try lerPorta();
    config.tls = try configurarTLS();
    errdefer config.tls.deinit();

    return config;
}

Cada passo pode falhar, e errdefer garante cleanup de todos os passos anteriores.

Padrões de Composição

Como compor funcionalidade sem herança?

Zig usa composição de structs e embeddings:

const Logger = struct {
    pub fn log(self: *Logger, msg: []const u8) void { ... }
};

const HttpServer = struct {
    logger: Logger,
    config: Config,

    pub fn handleRequest(self: *HttpServer, req: Request) !Response {
        self.logger.log("Recebida requisição");
        // ...
    }
};

Explique o padrão Sentinel.

Zig usa sentinels para marcar o fim de sequências — similar a null-terminated strings em C, mas generalizado:

// Slice com sentinel 0
const str: [:0]const u8 = "hello"; // null-terminated
const arr: [5:0]u8 = .{ 1, 2, 3, 4, 5 }; // sentinel 0 após o último

Sentinels são verificados em tempo de compilação, eliminando classes de bugs de C.

Padrões Avançados

Como implementar o padrão Observer sem herança?

Em Zig, o padrão Observer é implementado com callbacks armazenados em uma lista de function pointers, sem necessidade de classes abstratas:

const EventoTipo = enum { conectado, desconectado, mensagem_recebida };

const Listener = struct {
    ptr: *anyopaque,
    callbackFn: *const fn (*anyopaque, EventoTipo) void,

    pub fn notificar(self: Listener, evento: EventoTipo) void {
        self.callbackFn(self.ptr, evento);
    }
};

const EventBus = struct {
    listeners: std.ArrayList(Listener),

    pub fn publicar(self: *EventBus, evento: EventoTipo) void {
        for (self.listeners.items) |l| l.notificar(evento);
    }
};

Esse padrão é mais explícito que herança virtual: o tamanho da vtable é visível, o overhead é previsível e não há despacho dinâmico oculto.

O que é o padrão “tagged union” para polimorfismo de dados?

Tagged unions em Zig permitem representar valores de tipos diferentes de forma segura e eficiente:

const Expressao = union(enum) {
    numero: f64,
    soma: struct { esq: *Expressao, dir: *Expressao },
    produto: struct { esq: *Expressao, dir: *Expressao },

    pub fn avaliar(self: Expressao) f64 {
        return switch (self) {
            .numero => |n| n,
            .soma => |s| s.esq.avaliar() + s.dir.avaliar(),
            .produto => |p| p.esq.avaliar() * p.dir.avaliar(),
        };
    }
};

O compilador garante que todos os casos do switch sejam tratados, eliminando a classe de bugs de “case não tratado” comum em linguagens com polimorfismo dinâmico. Este padrão é fundamental para implementar ASTs, parsers e interpretadores em Zig.

Dicas de Entrevista para Design Patterns

Em entrevistas seniores, as perguntas sobre design patterns em Zig raramente pedem a implementação do padrão em si — elas avaliam se você entende por que certos padrões do mundo OOP não se aplicam a Zig e quais são as alternativas idiomáticas. Quando o entrevistador perguntar sobre Singleton, por exemplo, a resposta esperada é que Zig tem variáveis globais explícitas ou constantes de arquivo que servem ao mesmo propósito sem o overhead e a complexidade de uma classe gerenciada. Quando perguntarem sobre Strategy Pattern, mostre como comptime e function pointers resolvem o problema de forma mais eficiente e sem despacho dinâmico implícito. Demonstre que você pensa em termos de composição de dados e funções, não de hierarquias de objetos — essa mentalidade é o que separa um desenvolvedor Zig idiomático de alguém que simplesmente traduz código OOP para Zig.

Preparação

Design patterns são testados em posições seniores. Estude o código-fonte da biblioteca padrão de Zig para exemplos de bons padrões. Combine com perguntas de error handling, memória e comptime. Explore o ecossistema para ver padrões em projetos reais e pratique com desafios.

Continue aprendendo Zig

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