---
title: "Cheatsheet: Command em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-command-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-command-em-zig.MD"
description: "Design pattern Command implementado em Zig: encapsular requisições como objetos, undo/redo, filas de comandos e macro recording. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Command em Zig

Design pattern Command implementado em Zig: encapsular requisições como objetos, undo/redo, filas de comandos e macro recording. Guia completo em português.


# Command em Zig

O padrão Command encapsula uma requisição como um objeto, permitindo parametrizar, enfileirar, registrar e desfazer operações. Em Zig, isso é implementado com **tagged unions** (para conjunto fixo de comandos) ou **interfaces via ponteiros de função** (para extensibilidade).

## Quando Usar

- Sistemas de undo/redo (editores, jogos)
- Filas de tarefas e job queues
- Logging de operações para replay
- Macros e gravação de ações do usuário
- Transações que precisam ser revertíveis

## Implementação com Tagged Union

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

const Comando = union(enum) {
    inserir: struct { posicao: usize, texto: []const u8 },
    deletar: struct { posicao: usize, tamanho: usize },
    substituir: struct { posicao: usize, antigo: []const u8, novo: []const u8 },

    pub fn executar(self: Comando, buffer: *std.ArrayList(u8)) !void {
        switch (self) {
            .inserir => |cmd| {
                try buffer.insertSlice(cmd.posicao, cmd.texto);
            },
            .deletar => |cmd| {
                buffer.replaceRange(cmd.posicao, cmd.tamanho, &.{});
            },
            .substituir => |cmd| {
                buffer.replaceRange(cmd.posicao, cmd.antigo.len, cmd.novo);
            },
        }
    }

    pub fn desfazer(self: Comando, buffer: *std.ArrayList(u8)) !void {
        switch (self) {
            .inserir => |cmd| {
                buffer.replaceRange(cmd.posicao, cmd.texto.len, &.{});
            },
            .deletar => |cmd| {
                _ = cmd;
                // Precisaria do texto deletado salvo para desfazer
            },
            .substituir => |cmd| {
                buffer.replaceRange(cmd.posicao, cmd.novo.len, cmd.antigo);
            },
        }
    }
};

const EditorHistorico = struct {
    historico: std.ArrayList(Comando),
    posicao: usize = 0,

    pub fn init(allocator: std.mem.Allocator) EditorHistorico {
        return .{
            .historico = std.ArrayList(Comando).init(allocator),
        };
    }

    pub fn deinit(self: *EditorHistorico) void {
        self.historico.deinit();
    }

    pub fn executar(self: *EditorHistorico, cmd: Comando, buffer: *std.ArrayList(u8)) !void {
        try cmd.executar(buffer);
        // Descartar comandos após posição atual (novo branch)
        self.historico.shrinkRetainingCapacity(self.posicao);
        try self.historico.append(cmd);
        self.posicao += 1;
    }

    pub fn desfazer(self: *EditorHistorico, buffer: *std.ArrayList(u8)) !void {
        if (self.posicao == 0) return;
        self.posicao -= 1;
        try self.historico.items[self.posicao].desfazer(buffer);
    }

    pub fn refazer(self: *EditorHistorico, buffer: *std.ArrayList(u8)) !void {
        if (self.posicao >= self.historico.items.len) return;
        try self.historico.items[self.posicao].executar(buffer);
        self.posicao += 1;
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const alloc = gpa.allocator();

    var buffer = std.ArrayList(u8).init(alloc);
    defer buffer.deinit();

    var editor = EditorHistorico.init(alloc);
    defer editor.deinit();

    try editor.executar(.{ .inserir = .{ .posicao = 0, .texto = "Olá Mundo" } }, &buffer);
    std.debug.print("Após inserir: '{s}'\n", .{buffer.items});

    try editor.desfazer(&buffer);
    std.debug.print("Após desfazer: '{s}'\n", .{buffer.items});

    try editor.refazer(&buffer);
    std.debug.print("Após refazer: '{s}'\n", .{buffer.items});
}
```

## Fila de Comandos

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

const Tarefa = union(enum) {
    enviar_email: struct { para: []const u8, assunto: []const u8 },
    processar_imagem: struct { caminho: []const u8, largura: u32 },
    gerar_relatorio: struct { periodo: []const u8 },

    pub fn executar(self: Tarefa) void {
        switch (self) {
            .enviar_email => |t| std.debug.print("Enviando email para {s}: {s}\n", .{ t.para, t.assunto }),
            .processar_imagem => |t| std.debug.print("Processando {s} ({d}px)\n", .{ t.caminho, t.largura }),
            .gerar_relatorio => |t| std.debug.print("Gerando relatório: {s}\n", .{t.periodo}),
        }
    }
};

const FilaTarefas = struct {
    fila: std.ArrayList(Tarefa),

    pub fn init(allocator: std.mem.Allocator) FilaTarefas {
        return .{ .fila = std.ArrayList(Tarefa).init(allocator) };
    }

    pub fn deinit(self: *FilaTarefas) void {
        self.fila.deinit();
    }

    pub fn enfileirar(self: *FilaTarefas, tarefa: Tarefa) !void {
        try self.fila.append(tarefa);
    }

    pub fn processarTodas(self: *FilaTarefas) void {
        for (self.fila.items) |tarefa| {
            tarefa.executar();
        }
        self.fila.clearRetainingCapacity();
    }
};
```

## Considerações de Performance

- **Tagged unions vs ponteiros de função**: a abordagem com `union(enum)` é preferível quando o conjunto de comandos é fixo e conhecido em compile time. O compilador gera um `switch` eficiente sem indireção de ponteiro. Ponteiros de função são mais flexíveis para conjuntos abertos de comandos, mas introduzem indireção por chamada.
- **Histórico de undo com allocator**: cada `Comando` no histórico pode carregar dados de tamanho variável (texto a ser desfeito, estado anterior). Use um `ArenaAllocator` para o histórico — ao fazer "clear" do histórico, basta liberar a arena inteira em vez de desalocar comando por comando.
- **Fila de tarefas**: ao usar o padrão como job queue, dimensione o `ArrayList` com `ensureTotalCapacity` na inicialização para evitar realocações durante a execução. Em sistemas de alta frequência, considere uma fila circular estática (comptime `tamanho`).

## Erros Comuns

**Não salvar o estado anterior para desfazer**: a operação `desfazer` de um comando `deletar` precisa ter os dados que foram deletados. Se você não salvar o conteúdo antes da deleção, o undo é impossível. Armazene o estado anterior no próprio comando no momento da criação.

**Histórico ilimitado causando uso excessivo de memória**: em editores de longa sessão, um histórico sem limite pode consumir gigabytes. Implemente um limite (ex: últimos 100 comandos) e descarte os mais antigos ao exceder.

**Executar comandos diretamente sem passar pelo histórico**: se o código chamar a operação diretamente (sem `EditorHistorico.executar`), o histórico fica dessincronizado e o undo vai desfazer o comando errado.

## Perguntas Frequentes

**Qual é a diferença entre Command e Strategy?**
O Strategy substitui um algoritmo inteiro que é executado repetidamente. O Command representa uma operação única que pode ser armazenada, enfileirada ou desfeita. Um Command pode *usar* uma Strategy para decidir como executar.

**Como serializar comandos para persistência (ex: salvar sessão)?**
Com `union(enum)`, todos os campos têm tipos concretos conhecidos — use `std.json.stringify` diretamente. Para deserializar, leia o campo tag primeiro e faça `switch` para reconstruir o union correto.

**É possível compor múltiplos comandos em um macro-comando?**
Sim. Crie um variant `macro: std.ArrayList(Comando)` na union. O `executar` do macro itera e executa cada sub-comando. O `desfazer` itera na ordem inversa.

## Quando Evitar

- Operações simples sem necessidade de undo ou enfileiramento
- Quando o overhead de encapsular cada ação não se justifica
- Sistemas onde a ordem de execução não importa

## Veja Também

- [State Machine](/padroes/state-machine/) — Transições de estado estruturadas
- [Observer](/padroes/observer/) — Notificar após executar comandos
- [Pipeline](/padroes/pipeline/) — Processamento sequencial de etapas
- [Producer-Consumer](/padroes/producer-consumer/) — Fila assíncrona de tarefas
- [Error Handling](/cheatsheets/error-handling/) — Erros em comandos
