---
title: "Cheatsheet: Factory em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-factory-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-factory-em-zig.MD"
description: "Design pattern Factory implementado em Zig: criação de objetos via comptime, factory functions, abstract factory e padrões idiomáticos. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Factory em Zig

Design pattern Factory implementado em Zig: criação de objetos via comptime, factory functions, abstract factory e padrões idiomáticos. Guia completo em português.


# Factory em Zig

O padrão Factory encapsula a lógica de criação de objetos, permitindo que o código cliente crie instâncias sem conhecer os detalhes de implementação. Em Zig, esse padrão é elegantemente implementado usando **comptime**, **tagged unions** e **funções de inicialização**.

## Quando Usar

- Criação de objetos depende de configuração ou parâmetros de runtime
- Diferentes implementações de uma mesma interface
- Encapsular lógica complexa de inicialização
- Criar objetos para diferentes plataformas ou backends

## Factory Function Simples

```zig
const std = @import("std");

const Transporte = union(enum) {
    carro: Carro,
    bicicleta: Bicicleta,
    onibus: Onibus,

    const Carro = struct { velocidade_max: u32 = 120, assentos: u8 = 5 };
    const Bicicleta = struct { velocidade_max: u32 = 30, assentos: u8 = 1 };
    const Onibus = struct { velocidade_max: u32 = 80, assentos: u8 = 40 };

    pub fn criar(tipo: []const u8) !Transporte {
        if (std.mem.eql(u8, tipo, "carro")) return .{ .carro = .{} };
        if (std.mem.eql(u8, tipo, "bicicleta")) return .{ .bicicleta = .{} };
        if (std.mem.eql(u8, tipo, "onibus")) return .{ .onibus = .{} };
        return error.TipoDesconhecido;
    }

    pub fn velocidadeMax(self: Transporte) u32 {
        return switch (self) {
            .carro => |c| c.velocidade_max,
            .bicicleta => |b| b.velocidade_max,
            .onibus => |o| o.velocidade_max,
        };
    }

    pub fn assentos(self: Transporte) u8 {
        return switch (self) {
            .carro => |c| c.assentos,
            .bicicleta => |b| b.assentos,
            .onibus => |o| o.assentos,
        };
    }
};

pub fn main() !void {
    const t = try Transporte.criar("carro");
    std.debug.print("Velocidade máx: {d} km/h\n", .{t.velocidadeMax()});
    std.debug.print("Assentos: {d}\n", .{t.assentos()});
}
```

## Factory Genérica com comptime

```zig
const std = @import("std");

fn Serializador(comptime formato: []const u8) type {
    if (std.mem.eql(u8, formato, "json")) {
        return struct {
            pub fn serializar(dados: anytype, writer: anytype) !void {
                try std.json.stringify(dados, .{}, writer);
            }
        };
    } else if (std.mem.eql(u8, formato, "csv")) {
        return struct {
            pub fn serializar(dados: anytype, writer: anytype) !void {
                const fields = std.meta.fields(@TypeOf(dados));
                inline for (fields, 0..) |campo, i| {
                    if (i > 0) try writer.writeAll(",");
                    try writer.print("{any}", .{@field(dados, campo.name)});
                }
                try writer.writeAll("\n");
            }
        };
    } else {
        @compileError("Formato não suportado: " ++ formato);
    }
}

// Uso — tipo resolvido em comptime
const JsonSerializer = Serializador("json");
const CsvSerializer = Serializador("csv");
```

## Factory com Allocator

```zig
const std = @import("std");

const Conexao = struct {
    host: []const u8,
    porta: u16,
    allocator: std.mem.Allocator,

    pub fn criar(allocator: std.mem.Allocator, config: Config) !*Conexao {
        const conn = try allocator.create(Conexao);
        conn.* = .{
            .host = try allocator.dupe(u8, config.host),
            .porta = config.porta,
            .allocator = allocator,
        };
        return conn;
    }

    pub fn destruir(self: *Conexao) void {
        self.allocator.free(self.host);
        self.allocator.destroy(self);
    }
};

const Config = struct {
    host: []const u8 = "localhost",
    porta: u16 = 5432,
    tipo: []const u8 = "postgres",
};

fn criarConexao(allocator: std.mem.Allocator, config: Config) !*Conexao {
    // Factory pode escolher implementação baseada no config
    return Conexao.criar(allocator, config);
}
```

## Considerações de Performance

- **Factory com comptime é custo zero**: `Serializador("json")` resolve o tipo inteiro em tempo de compilação. O código final é idêntico ao que você escreveria manualmente — sem overhead de despacho dinâmico.
- **Factory com allocator**: cada chamada a `Conexao.criar` faz uma alocação. Se você cria conexões frequentemente, considere um [Pool de Objetos](/padroes/pool-objetos/) como backend — a factory pode checar o pool antes de alocar.
- **Tagged union como factory result**: retornar uma `union(enum)` em vez de um ponteiro alocado é frequentemente mais eficiente. A union vive na stack, não precisa de `destroy`, e o compilador pode otimizar os casos estáticos.

## Erros Comuns

**Factory que retorna `*T` quando poderia retornar `T`**: alocar um objeto com `allocator.create(T)` quando o objeto poderia viver na stack do caller adiciona overhead desnecessário. Retorne o valor diretamente (`T`) quando possível — use ponteiros apenas quando o objeto precisa sobreviver ao escopo da função criadora ou quando é genuinamente grande demais para a stack.

**Não implementar `deinit` correspondente**: toda factory que usa `allocator.create` ou `allocator.dupe` deve ter uma função correspondente de destruição (`destruir`, `deinit`, `destroy`). Documente claramente quem é responsável por liberar o objeto criado.

**Usar `@compileError` com mensagem ruim**: ao validar parâmetros comptime, escreva mensagens de erro claras: `@compileError("Formato não suportado: '" ++ formato ++ "'. Use \"json\" ou \"csv\".")`. Uma mensagem vaga vai custar tempo de depuração.

## Perguntas Frequentes

**Quando usar Factory vs construção direta `.{ .campo = valor }`?**
Use a construção direta quando os campos são simples e o caller pode fornecer todos os valores sem lógica adicional. Use Factory quando há validação, defaults complexos, lógica de decisão baseada em parâmetros, ou quando o tipo exato depende de condições de runtime.

**Como implementar um "Abstract Factory" em Zig?**
Combine comptime com tagged unions. Um tipo `Backend` pode ser uma `union(enum)` com variantes para cada implementação. Uma função factory recebe um parâmetro de configuração e retorna o `Backend` correto. O `switch` sobre a union garante que todos os casos sejam tratados.

**Factory pode retornar tipos diferentes dependendo dos parâmetros?**
Com comptime, sim — `if (std.mem.eql(u8, formato, "json")) return JsonType else return CsvType`. Em runtime, a forma idiomática é retornar uma `union(enum)` que contém um dos tipos possíveis.

## Quando Evitar

- Objetos simples que não precisam de lógica de criação complexa
- Quando a construção direta com `.{ ... }` é clara o suficiente
- Abstrações desnecessárias que adicionam complexidade sem benefício

## Veja Também

- [Builder](/padroes/builder/) — Construção passo a passo de objetos complexos
- [Singleton](/padroes/singleton/) — Garantir instância única
- [Comptime](/cheatsheets/comptime/) — Factory usando metaprogramação
- [Structs](/cheatsheets/structs/) — Inicialização de structs em Zig
- [Enums e Unions](/cheatsheets/enums-unions/) — Tagged unions para polimorfismo
