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.

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.