Errdefer em Zig — O que é e Como Usar

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

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:

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

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 errdefers 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.

Termos Relacionados

  • Defer — Execução garantida ao sair do escopo
  • Error Union — Tipo que carrega valor ou erro
  • Try — Propagação automática de erros
  • Allocator — Abstração de alocação de memória
  • RAII em Zig — Padrão de gerenciamento de recursos

Tutoriais Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.