---
title: "Errdefer em Zig — O que é e Como Usar"
url: "https://ziglang.com.br/glossario/errdefer-em-zig-o-que-%C3%A9-e-como-usar/"
markdown_url: "https://ziglang.com.br/glossario/errdefer-em-zig-o-que-%C3%A9-e-como-usar.MD"
description: "Entenda errdefer em Zig: o defer condicional que executa apenas quando há erro. Essencial para gerenciamento seguro de recursos. Guia em pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# Errdefer em Zig — O que é e Como Usar

Entenda errdefer em Zig: o defer condicional que executa apenas quando há erro. Essencial para gerenciamento seguro de recursos. Guia em pt-BR.


# Errdefer em Zig — O que é e Como Usar

## Definição

O **`errdefer`** em Zig é uma variante do `defer` que executa o código agendado **apenas quando o escopo é abandonado por causa de um erro**. Se a função retorna com sucesso, o `errdefer` é ignorado. Esse mecanismo é fundamental para construir funções que alocam múltiplos recursos e precisam desfazer alocações parciais em caso de falha.

## Por que Errdefer Importa

1. **Alocação parcial segura**: Quando uma função aloca vários recursos em sequência, `errdefer` garante que os já alocados sejam liberados se um dos passos falhar.
2. **Evita vazamentos de memória**: Sem `errdefer`, erros no meio de uma função podem deixar recursos sem liberação.
3. **Código mais legível**: A lógica de limpeza fica junto da alocação, não em blocos de tratamento de erro distantes.
4. **Complementa o `defer`**: Enquanto `defer` é para limpeza incondicional, `errdefer` é para reverter operações.

## Exemplo Prático

### Padrão de Inicialização Segura

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

const Servidor = struct {
    socket: std.posix.socket_t,
    buffer: []u8,
    log: std.fs.File,

    pub fn init(allocator: std.mem.Allocator) !Servidor {
        // Passo 1: abrir socket
        const socket = try std.posix.socket(std.posix.AF.INET, std.posix.SOCK.STREAM, 0);
        errdefer std.posix.close(socket); // Fecha se passos seguintes falharem

        // Passo 2: alocar buffer
        const buffer = try allocator.alloc(u8, 4096);
        errdefer allocator.free(buffer); // Libera se passo 3 falhar

        // Passo 3: abrir arquivo de log
        const log = try std.fs.cwd().createFile("servidor.log", .{});
        // Sem errdefer aqui — se chegamos aqui, tudo deu certo

        return Servidor{
            .socket = socket,
            .buffer = buffer,
            .log = log,
        };
    }

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

Se `createFile` falhar, o buffer é liberado e o socket é fechado automaticamente. Se `alloc` falhar, apenas o socket é fechado. Cada `errdefer` protege contra falhas nos passos subsequentes.

### Errdefer com Captura de Erro

A partir de versões recentes do Zig, `errdefer` pode capturar o erro:

```zig
fn processar() !void {
    const recurso = try adquirirRecurso();
    errdefer |err| {
        std.log.err("Falha ao processar: {}", .{err});
        recurso.liberar();
    };

    try etapa1();
    try etapa2();
    try etapa3();
}
```

A variável `err` contém o erro que causou a saída do escopo, permitindo logging detalhado.

## Quando Usar Defer vs Errdefer

```zig
fn exemplo(allocator: std.mem.Allocator) !*Dados {
    const dados = try allocator.create(Dados);

    // ERRADO: defer destruiria mesmo no retorno com sucesso!
    // defer allocator.destroy(dados);

    // CERTO: errdefer só destrói se houver erro
    errdefer allocator.destroy(dados);

    dados.* = try Dados.inicializar();
    return dados; // Chamador agora é dono da memória
}
```

A regra é simples: se a função **retorna** o recurso ao chamador, use `errdefer`. Se a função **consome** o recurso internamente, use `defer`.

## Armadilhas Comuns

- **Usar `defer` onde deveria ser `errdefer`**: Se a função retorna um recurso alocado, `defer` o destruiria antes do chamador usá-lo.
- **Esquecer o `errdefer` em cadeias de alocação**: Cada recurso alocado antes de um possível `try` precisa de seu próprio `errdefer`.
- **Ordem dos errdefers**: Assim como `defer`, os `errdefer`s executam em ordem LIFO. Certifique-se de que a ordem de limpeza faz sentido.
- **Não usar quando não há transferência de propriedade**: Se o recurso será liberado na mesma função, `defer` é suficiente.

## Casos de Uso

O `errdefer` resolve um problema clássico: como garantir que recursos alocados em etapas sejam liberados corretamente quando uma das etapas falha?

**Inicialização em múltiplos passos** é o caso de uso principal. Considere um cliente de banco de dados que aloca conexão, prepara statements e cria um pool de workers:

```zig
const DbCliente = struct {
    conn: *Conexao,
    stmt_pool: []Statement,
    workers: []Worker,

    pub fn init(allocator: std.mem.Allocator) !DbCliente {
        const conn = try Conexao.abrir("postgres://localhost/db");
        errdefer conn.fechar();

        const stmt_pool = try alocaStatements(allocator, conn, 10);
        errdefer liberaStatements(allocator, stmt_pool);

        const workers = try iniciaWorkers(allocator, 4);
        // Sem errdefer aqui — se chegamos, tudo OK

        return .{
            .conn = conn,
            .stmt_pool = stmt_pool,
            .workers = workers,
        };
    }

    pub fn deinit(self: *DbCliente, allocator: std.mem.Allocator) void {
        liberaWorkers(allocator, self.workers);
        liberaStatements(allocator, self.stmt_pool);
        self.conn.fechar();
    }
};
```

Cada `errdefer` protege exatamente o que foi alocado até aquele ponto. Se `iniciaWorkers` falhar, apenas os recursos dos passos anteriores são limpos — nem mais nem menos.

## Boas Práticas

- **Sempre pareie `errdefer` com alocações antes de `try`**: Cada `try` é um ponto de saída potencial por erro; cada alocação antes de um `try` precisa de proteção.
- **Use `errdefer |err|` para diagnóstico**: Capturar o erro no errdefer permite emitir mensagens de log detalhadas que facilitam o debug.
- **Não use `errdefer` quando `defer` é suficiente**: Se o recurso é consumido inteiramente dentro da função, `defer` é mais simples e correto.

## Termos Relacionados

- [Defer](/glossario/defer/) — Execução garantida ao sair do escopo
- [Error Union](/glossario/error-union/) — Tipo que carrega valor ou erro
- [Try](/glossario/try/) — Propagação automática de erros
- [Allocator](/glossario/allocator/) — Abstração de alocação de memória
- [RAII em Zig](/glossario/raii-zig/) — Padrão de gerenciamento de recursos

## Tutoriais Relacionados

- [Tratamento de Erros em Zig](/tutoriais/tratamento-de-erros-em-zig/)
- [Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)
- [Zig Design Patterns](/tutoriais/zig-design-patterns/)
