---
title: "Cheatsheet: Decorator em Zig"
url: "https://ziglang.com.br/padroes/cheatsheet-decorator-em-zig/"
markdown_url: "https://ziglang.com.br/padroes/cheatsheet-decorator-em-zig.MD"
description: "Design pattern Decorator implementado em Zig: adicionar comportamento a objetos via composição, wrapping de writers/readers e middleware. Guia completo em português."
date: "2026-02-21"
author: "Zig Brasil"
---

# Cheatsheet: Decorator em Zig

Design pattern Decorator implementado em Zig: adicionar comportamento a objetos via composição, wrapping de writers/readers e middleware. Guia completo em português.


# Decorator em Zig

O padrão Decorator permite adicionar comportamento a objetos individualmente sem afetar outros objetos da mesma classe. Em Zig, este padrão é naturalmente implementado com **composição e wrapping**, especialmente visível no design de readers/writers da biblioteca padrão que são compostos em camadas.

## Quando Usar

- Adicionar logging, métricas ou caching a operações existentes
- Compor camadas de I/O (buffering, compressão, criptografia)
- Middleware em servidores web
- Adicionar validação ou transformação sem alterar o código original

## Writer Decorado

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

fn LoggingWriter(comptime WriterType: type) type {
    return struct {
        const Self = @This();
        writer_interno: WriterType,
        bytes_escritos: *usize,

        pub const Error = WriterType.Error;
        pub const Writer = std.io.Writer(*Self, Error, write);

        pub fn writer(self: *Self) Writer {
            return .{ .context = self };
        }

        fn write(self: *Self, bytes: []const u8) Error!usize {
            const n = try self.writer_interno.write(bytes);
            self.bytes_escritos.* += n;
            return n;
        }
    };
}

fn loggingWriter(inner_writer: anytype, bytes_counter: *usize) LoggingWriter(@TypeOf(inner_writer)) {
    return .{
        .writer_interno = inner_writer,
        .bytes_escritos = bytes_counter,
    };
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    var bytes_total: usize = 0;
    var logging = loggingWriter(stdout, &bytes_total);
    const writer = logging.writer();

    try writer.writeAll("Olá, mundo!\n");
    try writer.print("Número: {d}\n", .{42});

    // O decorator contou os bytes sem alterar o comportamento do writer
    std.debug.print("Total de bytes escritos: {d}\n", .{bytes_total});
}
```

## Decorator de Função

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

fn comTiming(comptime func: anytype) @TypeOf(func) {
    return struct {
        fn wrapper(args: anytype) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
            var timer = std.time.Timer.start() catch unreachable;

            const resultado = @call(.auto, func, args);

            const elapsed = timer.read();
            std.debug.print("[TIMING] {d}ms\n", .{elapsed / std.time.ns_per_ms});

            return resultado;
        }
    }.wrapper;
}

fn operacaoLenta() void {
    std.time.sleep(50 * std.time.ns_per_ms);
}

// const operacaoComTiming = comTiming(operacaoLenta);
```

## Middleware Pattern

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

const Requisicao = struct {
    path: []const u8,
    metodo: []const u8,
    headers: std.StringHashMap([]const u8),
};

const Resposta = struct {
    status: u16 = 200,
    corpo: []const u8 = "",
};

const Handler = *const fn (*Requisicao) Resposta;
const Middleware = *const fn (*Requisicao, Handler) Resposta;

fn comLog(req: *Requisicao, proximo: Handler) Resposta {
    std.debug.print("[LOG] {s} {s}\n", .{ req.metodo, req.path });
    const resp = proximo(req);
    std.debug.print("[LOG] Status: {d}\n", .{resp.status});
    return resp;
}

fn comAuth(req: *Requisicao, proximo: Handler) Resposta {
    if (!req.headers.contains("Authorization")) {
        return .{ .status = 401, .corpo = "Não autorizado" };
    }
    return proximo(req);
}

fn handlerFinal(req: *Requisicao) Resposta {
    _ = req;
    return .{ .status = 200, .corpo = "OK" };
}
```

## Decorator de Validação com Error Union

Um caso de uso prático é decorar funções de processamento com validação prévia, tirando proveito de error unions:

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

fn comValidacao(
    comptime T: type,
    comptime validar: fn (T) bool,
    comptime processar: fn (T) !void,
) fn (T) !void {
    return struct {
        fn wrapper(entrada: T) !void {
            if (!validar(entrada)) return error.EntradaInvalida;
            return processar(entrada);
        }
    }.wrapper;
}

fn emailValido(email: []const u8) bool {
    return std.mem.indexOfScalar(u8, email, '@') != null;
}

fn enviarEmail(email: []const u8) !void {
    std.debug.print("Enviando para: {s}\n", .{email});
}

const enviarComValidacao = comValidacao([]const u8, emailValido, enviarEmail);

pub fn main() !void {
    try enviarComValidacao("usuario@exemplo.com"); // ok
    enviarComValidacao("email-invalido") catch |err| {
        std.debug.print("Erro: {}\n", .{err}); // error.EntradaInvalida
    };
}
```

## Considerações de Performance

- **Writers compostos da stdlib**: `std.io.bufferedWriter` e companhia usam exatamente o padrão Decorator. O compilador Zig é capaz de inlinar chamadas em cadeias de writers genéricos (`comptime WriterType`), eliminando o overhead de indireção quando os tipos são conhecidos em compile time.
- **Decorator com `anyopaque`**: se você usar ponteiros opacos para composição em runtime (como no middleware HTTP), há uma indireção por chamada. Aceitável para código de servidor, mas evite em loops de processamento de alta frequência.
- **Contar bytes e logging**: decorators simples como `LoggingWriter` têm overhead de uma adição e uma chamada de print por `write`. Para logs de debug, use `if (builtin.mode == .Debug)` para eliminar o decorator completamente em builds de produção.

## Erros Comuns

**Propagar o tipo de erro incorretamente**: ao criar um `LoggingWriter`, o `Error` do decorator deve ser exatamente igual ao `Error` do writer interno. Se você adicionar `error.FalhaNaMetrica` ao conjunto de erros, o caller vai precisar tratar um erro que nunca ocorre na prática.

**Esquecer de propagar o retorno do writer interno**: no método `write`, sempre retorne o valor `n` do writer interno — não retorne `bytes.len`. O writer interno pode ter escrito menos bytes que o solicitado, e o caller precisa saber disso.

**Criar decorators com estado mutável compartilhado**: se o decorator guarda um contador ou buffer, e múltiplas goroutines usam o mesmo writer decorado, você precisa de sincronização. A alternativa mais simples em Zig é criar um decorator por thread.

## Perguntas Frequentes

**Qual é a diferença entre Decorator e Wrapper?**
Na prática, são a mesma coisa em Zig. "Decorator" enfatiza a adição de comportamento mantendo a mesma interface. "Wrapper" é o termo mais genérico para qualquer struct que envolve outra.

**Posso decorar structs que não implementam a interface Writer?**
Sim — o padrão funciona para qualquer interface. Crie seu próprio tipo de "interface" com `ptr: *anyopaque` e ponteiros de função, e aplique o mesmo padrão de composição. Veja o padrão Type Erasure para a técnica completa.

**Como depurar uma cadeia profunda de decorators?**
Adicione um campo `nome: []const u8` a cada decorator e imprima-o nos métodos. Em modo de debug, isso ajuda a rastrear por onde os dados passam. Remova antes de produção ou guarde por trás de `if (builtin.mode == .Debug)`.

## Quando Evitar

- Quando apenas uma variante é necessária (composição direta é mais simples)
- Muitas camadas de decoração que dificultam depuração
- Quando performance é crítica e cada camada adiciona overhead

## Veja Também

- [Adapter](/padroes/adapter/) — Adaptar interfaces incompatíveis
- [Facade](/padroes/facade/) — Simplificar interface complexa
- [Pipeline](/padroes/pipeline/) — Processamento em estágios
- [Strategy](/padroes/strategy/) — Trocar algoritmo inteiro
- [Operações I/O](/cheatsheets/io-operacoes/) — Writers/Readers composíveis
