---
title: "Padrões Errdefer para Cleanup em Zig"
url: "https://ziglang.com.br/receitas/padr%C3%B5es-errdefer-para-cleanup-em-zig/"
markdown_url: "https://ziglang.com.br/receitas/padr%C3%B5es-errdefer-para-cleanup-em-zig.MD"
description: "Receita prática de padrões errdefer em Zig para cleanup condicional de recursos. Alocação segura, inicialização em múltiplos passos, transações e resource acquisition."
date: "2026-02-21"
author: "Zig Brasil"
---

# Padrões Errdefer para Cleanup em Zig

Receita prática de padrões errdefer em Zig para cleanup condicional de recursos. Alocação segura, inicialização em múltiplos passos, transações e resource acquisition.


## Introdução

`errdefer` é uma das features mais elegantes de Zig. Diferente de `defer` (que executa sempre), `errdefer` executa apenas quando a função retorna um erro. Isso permite escrever código de inicialização que limpa recursos parcialmente alocados em caso de falha, sem precisar de blocos try/finally ou goto cleanup como em C.

Para error handling geral, veja [Padrões Try/Catch](/receitas/zig-try-catch-erros/) e [Error Sets Customizados](/receitas/zig-error-set-customizado/).

## Pré-requisitos

- Zig instalado (versão 0.13+). Veja o [guia de instalação](/tutoriais/como-instalar-zig/)
- Conhecimento básico de Zig. Consulte a [introdução ao Zig](/tutoriais/introducao-ao-zig/)

## Padrão Básico: Alocação com Cleanup

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

fn criarBuffer(allocator: std.mem.Allocator, tamanho: usize) ![]u8 {
    const buffer = try allocator.alloc(u8, tamanho);
    errdefer allocator.free(buffer); // Libera SÓ se retornar erro

    // Se esta operação falhar, buffer é liberado automaticamente
    try preencherBuffer(buffer);

    return buffer; // Sucesso: errdefer NÃO executa
}
```

Sem `errdefer`, teríamos que fazer:
```zig
// SEM errdefer (como seria em C)
fn criarBuffer(allocator: std.mem.Allocator, tamanho: usize) ![]u8 {
    const buffer = try allocator.alloc(u8, tamanho);

    preencherBuffer(buffer) catch |err| {
        allocator.free(buffer); // Limpeza manual
        return err;
    };

    return buffer;
}
```

## Inicialização em Múltiplos Passos

```zig
const Servidor = struct {
    socket: std.posix.socket_t,
    config: Config,
    buffer: []u8,
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator, porta: u16) !Servidor {
        // Passo 1: Alocar buffer
        const buffer = try allocator.alloc(u8, 8192);
        errdefer allocator.free(buffer);

        // Passo 2: Carregar config (se falhar, buffer é liberado)
        const config = try Config.carregar(allocator);
        errdefer config.deinit();

        // Passo 3: Abrir socket (se falhar, config e buffer são liberados)
        const socket = try abrirSocket(porta);
        errdefer std.posix.close(socket);

        // Passo 4: Bind (se falhar, tudo acima é liberado)
        try bindSocket(socket, porta);

        // Sucesso: NENHUM errdefer executa
        return .{
            .socket = socket,
            .config = config,
            .buffer = buffer,
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Servidor) void {
        std.posix.close(self.socket);
        self.config.deinit();
        self.allocator.free(self.buffer);
    }
};
```

Se o passo 4 (`bindSocket`) falhar:
1. `errdefer std.posix.close(socket)` executa
2. `errdefer config.deinit()` executa
3. `errdefer allocator.free(buffer)` executa

A ordem de execução é reversa (LIFO), igual a `defer`.

## Errdefer com Captura de Erro

```zig
fn processar(allocator: std.mem.Allocator) !Resultado {
    const dados = try allocator.alloc(u8, 1024);
    errdefer |err| {
        std.log.err("Falha ao processar: {}", .{err});
        allocator.free(dados);
    };

    try etapa1(dados);
    try etapa2(dados);
    return Resultado{ .dados = dados };
}
```

## Padrão: Builder com Errdefer

```zig
const Pipeline = struct {
    etapas: std.ArrayList(Etapa),
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) Pipeline {
        return .{
            .etapas = std.ArrayList(Etapa).init(allocator),
            .allocator = allocator,
        };
    }

    pub fn deinit(self: *Pipeline) void {
        for (self.etapas.items) |*etapa| {
            etapa.deinit();
        }
        self.etapas.deinit();
    }

    pub fn adicionarEtapa(self: *Pipeline, config: EtapaConfig) !void {
        var etapa = try Etapa.init(self.allocator, config);
        errdefer etapa.deinit(); // Se append falhar, a etapa é limpa

        try self.etapas.append(etapa);
    }
};
```

## Padrão: Transação (Tudo ou Nada)

```zig
fn transferir(
    db: *Database,
    de: u32,
    para: u32,
    valor: f64,
) !void {
    try db.iniciarTransacao();
    errdefer db.rollback(); // Se QUALQUER passo falhar, rollback

    try db.debitar(de, valor);
    try db.creditar(para, valor);
    try db.registrarLog(de, para, valor);

    try db.commit(); // Sucesso: errdefer não executa
}
```

## Errdefer em Loops

```zig
fn criarWorkers(allocator: std.mem.Allocator, n: usize) ![]Worker {
    const workers = try allocator.alloc(Worker, n);
    errdefer allocator.free(workers);

    var inicializados: usize = 0;
    errdefer {
        // Limpar workers já inicializados em caso de falha
        for (workers[0..inicializados]) |*w| {
            w.deinit();
        }
    };

    for (workers) |*w| {
        w.* = try Worker.init(allocator);
        inicializados += 1;
    }

    return workers;
}
```

## Defer vs Errdefer: Quando Usar Cada

```zig
fn exemplo(allocator: std.mem.Allocator) !Resultado {
    // defer: SEMPRE executa (sucesso ou erro)
    // Usar para recursos que devem ser limpos em ambos os casos
    const arquivo = try std.fs.cwd().openFile("temp.txt", .{});
    defer arquivo.close(); // Sempre fechar o arquivo

    // errdefer: executa SÓ em erro
    // Usar para recursos que serão TRANSFERIDOS ao chamador em sucesso
    const dados = try allocator.alloc(u8, 1024);
    errdefer allocator.free(dados); // Liberar SÓ se falhar

    try arquivo.readAll(dados);

    return Resultado{ .dados = dados }; // Transfere ownership ao chamador
}
```

## Testes

```zig
test "errdefer libera em caso de erro" {
    const allocator = std.testing.allocator;

    // Forçar erro para verificar que errdefer limpa corretamente
    const resultado = criarBuffer(allocator, 0);

    // Se errdefer funcionar corretamente, testing.allocator
    // não detectará vazamento de memória
    try std.testing.expectError(error.TamanhoInvalido, resultado);
}
```

Veja [Testes com Allocator](/receitas/zig-teste-com-allocator/) para testar que errdefer funciona corretamente.

## Conclusão

`errdefer` é essencial para escrever código Zig robusto. Ele garante cleanup automático de recursos parcialmente alocados em caso de erro, sem a complexidade de goto cleanup (C) ou try/finally (Java/C#). Use `defer` para cleanup incondicional e `errdefer` para cleanup condicional ao erro.

Para mais sobre erros em Zig, veja [Padrões Try/Catch](/receitas/zig-try-catch-erros/), [Error Sets Customizados](/receitas/zig-error-set-customizado/) e [Error Logging](/receitas/zig-error-logging/).
