---
title: "Cheatsheet: Strategy em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-strategy-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-strategy-em-zig.MD"
description: "Design pattern Strategy implementado em Zig: algoritmos intercambiáveis via comptime, function pointers e tagged unions. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Strategy em Zig

Design pattern Strategy implementado em Zig: algoritmos intercambiáveis via comptime, function pointers e tagged unions. Guia completo em português.


# Strategy em Zig

O padrão Strategy define uma família de algoritmos, encapsula cada um deles e os torna intercambiáveis. Em Zig, existem três formas principais de implementar: **comptime** (zero custo em runtime), **ponteiros de função** (flexível em runtime) e **tagged unions** (type-safe com exaustividade verificada pelo compilador).

## Quando Usar

- Diferentes algoritmos de ordenação, compressão, criptografia
- Estratégias de retry, cache ou roteamento
- Formatação de saída (JSON, CSV, XML)
- Validação com regras configuráveis

## Strategy com comptime (Custo Zero)

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

fn Compressor(comptime estrategia: enum { gzip, lz4, nenhuma }) type {
    return struct {
        pub fn comprimir(dados: []const u8) []const u8 {
            return switch (estrategia) {
                .gzip => {
                    // lógica de compressão gzip
                    _ = dados;
                    return "dados_gzip";
                },
                .lz4 => {
                    _ = dados;
                    return "dados_lz4";
                },
                .nenhuma => dados,
            };
        }
    };
}

// Tipo resolvido em compilação — zero overhead
const CompressorGzip = Compressor(.gzip);
const CompressorLz4 = Compressor(.lz4);
```

## Strategy com Ponteiros de Função

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

const EstrategiaOrdenacao = *const fn ([]i32) void;

fn bubbleSort(dados: []i32) void {
    for (0..dados.len) |_| {
        for (0..dados.len - 1) |j| {
            if (dados[j] > dados[j + 1]) {
                const temp = dados[j];
                dados[j] = dados[j + 1];
                dados[j + 1] = temp;
            }
        }
    }
}

fn insertionSort(dados: []i32) void {
    for (1..dados.len) |i| {
        const chave = dados[i];
        var j: usize = i;
        while (j > 0 and dados[j - 1] > chave) {
            dados[j] = dados[j - 1];
            j -= 1;
        }
        dados[j] = chave;
    }
}

const Ordenador = struct {
    estrategia: EstrategiaOrdenacao,

    pub fn ordenar(self: *const Ordenador, dados: []i32) void {
        self.estrategia(dados);
    }

    pub fn setEstrategia(self: *Ordenador, nova: EstrategiaOrdenacao) void {
        self.estrategia = nova;
    }
};

pub fn main() void {
    var dados = [_]i32{ 5, 2, 8, 1, 9, 3 };
    var ordenador = Ordenador{ .estrategia = bubbleSort };

    ordenador.ordenar(&dados);
    // Trocar estratégia em runtime
    ordenador.setEstrategia(insertionSort);
}
```

## Strategy com Tagged Union

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

const FormatoSaida = union(enum) {
    json,
    csv: struct { separador: u8 = ',' },
    texto: struct { largura: u16 = 80 },

    pub fn formatar(self: FormatoSaida, dados: anytype, writer: anytype) !void {
        switch (self) {
            .json => {
                try std.json.stringify(dados, .{}, writer);
                try writer.writeAll("\n");
            },
            .csv => |opts| {
                _ = opts;
                // formatação CSV...
                try writer.writeAll("dados,csv\n");
            },
            .texto => |opts| {
                _ = opts;
                try writer.writeAll("Saída texto\n");
            },
        }
    }
};

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const formato = FormatoSaida.json;
    try formato.formatar(.{ .nome = "Zig" }, stdout);
}
```

## Strategy com Contexto e Estado

Quando a estratégia precisa de estado próprio (ex: uma estratégia de retry com contador interno), combine com o padrão de interface via `anyopaque`:

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

const EstrategiaCache = struct {
    ptr: *anyopaque,
    buscarFn: *const fn (*anyopaque, []const u8) ?[]const u8,
    armazenarFn: *const fn (*anyopaque, []const u8, []const u8) void,

    pub fn buscar(self: EstrategiaCache, chave: []const u8) ?[]const u8 {
        return self.buscarFn(self.ptr, chave);
    }

    pub fn armazenar(self: EstrategiaCache, chave: []const u8, valor: []const u8) void {
        self.armazenarFn(self.ptr, chave, valor);
    }
};

// Estratégia LRU com estado interno
const CacheLRU = struct {
    dados: std.StringHashMap([]const u8),
    capacidade: usize,

    pub fn init(allocator: std.mem.Allocator, cap: usize) CacheLRU {
        return .{ .dados = std.StringHashMap([]const u8).init(allocator), .capacidade = cap };
    }

    pub fn estrategia(self: *CacheLRU) EstrategiaCache {
        return .{ .ptr = self, .buscarFn = buscar, .armazenarFn = armazenar };
    }

    fn buscar(ptr: *anyopaque, chave: []const u8) ?[]const u8 {
        const self: *CacheLRU = @alignCast(@ptrCast(ptr));
        return self.dados.get(chave);
    }

    fn armazenar(ptr: *anyopaque, chave: []const u8, valor: []const u8) void {
        const self: *CacheLRU = @alignCast(@ptrCast(ptr));
        self.dados.put(chave, valor) catch {};
    }
};
```

## Considerações de Performance

- **Comptime strategy tem custo absoluto zero**: `Compressor(.gzip)` resulta em código totalmente especializado — não há despacho dinâmico, não há ponteiro de função. O compilador pode inlinar, vetorizar e otimizar como se o código tivesse sido escrito diretamente.
- **Ponteiro de função vs comptime**: a chamada via ponteiro de função (`self.estrategia(dados)`) impede inlining pelo compilador e evita que o preditor de branch funcione otimamente. Para estratégias chamadas bilhões de vezes (ordenação de dados, compressão), prefira comptime.
- **Tagged union tem branch prediction melhor**: para poucas estratégias (2-4), um `switch` sobre a union pode ser mais rápido que ponteiro de função porque o preditor de branch do processador aprende os padrões de acesso.
- **Trocar estratégia em runtime**: ponteiros de função e tagged unions permitem trocar a estratégia em runtime sem recompilar. Comptime não permite — a estratégia é fixada em tempo de compilação.

## Erros Comuns

**Usar ponteiro de função para estratégia que nunca muda**: se a estratégia de ordenação é sempre `bubbleSort` para um determinado tipo de dado, não há razão para usar ponteiro de função. Um `comptime` parameter ou chamada direta é mais eficiente e mais fácil de entender.

**Estratégia com estado compartilhado entre usuários**: se a estratégia tem estado interno (contadores, cache), e múltiplos objetos compartilham a mesma instância de estratégia via ponteiro, o estado é compartilhado. Isso pode ser intencional (cache compartilhado) ou um bug sutil (contador duplicado). Documente claramente.

**Tagged union sem exaustividade**: ao usar `else => unreachable` em vez de listar todos os casos, você perde a proteção do compilador. Quando adicionar uma nova estratégia à union, o compilador não vai alertar sobre os `switch` que precisam ser atualizados.

## Perguntas Frequentes

**Qual das três formas (comptime, ponteiro de função, tagged union) devo escolher?**
Use comptime quando a estratégia é conhecida em compile time e performance é crítica. Use tagged union quando o conjunto de estratégias é fixo e fechado — você ganha exaustividade verificada pelo compilador. Use ponteiros de função quando o conjunto de estratégias é aberto (plugins, extensões por terceiros) ou quando você precisa de estratégias com estado via `anyopaque`.

**Strategy é o mesmo que injeção de dependência de comportamento?**
Sim — Strategy é uma forma específica de Dependency Injection onde o que é injetado é um algoritmo ou comportamento, não um serviço. A diferença é principalmente de intenção: DI foca em desacoplar dependências de infraestrutura; Strategy foca em intercambiar algoritmos.

**Como comparar performance entre estratégias?**
Use `std.time.Timer` para medir o tempo de execução de cada estratégia com dados representativos do seu caso de uso real. Benchmarks artificiais frequentemente favorece a estratégia errada.

## Quando Evitar

- Quando só existe uma estratégia (e não se planeja extensão)
- Se comptime resolve o problema sem necessidade de troca em runtime
- Poucas variantes simples — um `switch` direto pode ser mais claro

## Veja Também

- [Factory](/padroes/factory/) — Criar a estratégia certa baseada em config
- [Observer](/padroes/observer/) — Notificar sobre mudança de estratégia
- [Type Erasure](/padroes/type-erasure/) — Interfaces genéricas em runtime
- [Comptime](/cheatsheets/comptime/) — Strategy resolvido na compilação
- [Enums e Unions](/cheatsheets/enums-unions/) — Tagged unions para estratégias
