---
title: "RAII em Zig — O que é e Como Usar"
url: "https://ziglang.com.br/glossario/raii-em-zig-o-que-%C3%A9-e-como-usar/"
markdown_url: "https://ziglang.com.br/glossario/raii-em-zig-o-que-%C3%A9-e-como-usar.MD"
description: "Entenda como Zig implementa o padrão RAII usando defer e errdefer para gerenciar recursos automaticamente. Sem destructors implícitos. Guia pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# RAII em Zig — O que é e Como Usar

Entenda como Zig implementa o padrão RAII usando defer e errdefer para gerenciar recursos automaticamente. Sem destructors implícitos. Guia pt-BR.


# RAII em Zig — O que é e Como Usar

## Definição

**RAII** (Resource Acquisition Is Initialization) é um padrão de programação onde a aquisição de um recurso está vinculada à sua liberação. Em C++ e Rust, isso é feito com destructors automáticos. Em Zig, o padrão RAII é implementado **explicitamente** usando `defer` e `errdefer` — o programador declara a limpeza imediatamente após a aquisição, e o compilador garante que ela será executada ao sair do escopo.

Zig deliberadamente evita destructors implícitos para manter o código explícito e previsível. A filosofia é: "se algo importante está acontecendo, deve ser visível no código."

## Por que RAII em Zig Importa

1. **Sem vazamentos**: `defer` garante liberação mesmo em caminhos de erro.
2. **Explícito**: A limpeza é visível no código, não escondida em destructors.
3. **Flexível**: `errdefer` libera apenas quando há erro — ideal para inicialização parcial.
4. **Composável**: Múltiplos `defer` executam em ordem reversa (LIFO).

## Exemplo Prático

### Padrão defer para Limpeza

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

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit(); // Liberado ao final da função

    const allocator = gpa.allocator();

    // Alocação + defer para liberação (RAII)
    const buffer = try allocator.alloc(u8, 1024);
    defer allocator.free(buffer);

    // Abrir arquivo + defer para fechar
    const arquivo = try std.fs.cwd().openFile("dados.txt", .{});
    defer arquivo.close();

    // Usar buffer e arquivo normalmente...
    const bytes_lidos = try arquivo.read(buffer);
    std.debug.print("Lidos: {} bytes\n", .{bytes_lidos});

    // Ao sair do escopo (normal ou erro), defers executam em ordem reversa:
    // 1. arquivo.close()
    // 2. allocator.free(buffer)
    // 3. gpa.deinit()
}
```

### errdefer para Inicialização Parcial

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

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

    pub fn init(allocator: std.mem.Allocator) !Servidor {
        // Alocar buffer
        const buffer = try allocator.alloc(u8, 4096);
        errdefer allocator.free(buffer); // Libera se o próximo passo falhar

        // Criar socket (pode falhar)
        const socket = try std.posix.socket(
            std.posix.AF.INET,
            std.posix.SOCK.STREAM,
            0,
        );
        errdefer std.posix.close(socket); // Fecha se os próximos passos falharem

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

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

### Padrão init/deinit (RAII Idiomático em Zig)

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

fn Lista(comptime T: type) type {
    return struct {
        items: std.ArrayList(T),

        const Self = @This();

        pub fn init(allocator: std.mem.Allocator) Self {
            return .{
                .items = std.ArrayList(T).init(allocator),
            };
        }

        pub fn deinit(self: *Self) void {
            self.items.deinit();
        }

        pub fn adicionar(self: *Self, valor: T) !void {
            try self.items.append(valor);
        }
    };
}

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

    var lista = Lista(u32).init(gpa.allocator());
    defer lista.deinit(); // RAII: limpeza garantida

    try lista.adicionar(10);
    try lista.adicionar(20);
}
```

## RAII em Zig vs C++ vs Rust

| Aspecto | C++ | Rust | Zig |
|---------|-----|------|-----|
| Mecanismo | Destructor (~Class) | Drop trait | defer/errdefer |
| Implícito | Sim | Sim | Nunca |
| Visível no código | Parcialmente | Parcialmente | Sempre |
| Ordem | Reversa | Reversa | Reversa |
| Condicional | Não | Não | Sim (errdefer) |

## Quando Usar errdefer vs defer

A distinção entre `errdefer` e `defer` é fundamental para inicialização correta de structs complexas:

- **`errdefer`** fica na função que *constrói* o recurso: ele desfaz a construção parcial se algo der errado.
- **`defer`** fica no *chamador* que recebeu o recurso com sucesso: ele garante a limpeza ao sair do escopo.

```zig
// Na função init: use errdefer
pub fn init(allocator: std.mem.Allocator) !Recurso {
    const a = try allocator.alloc(u8, 100);
    errdefer allocator.free(a); // Libera 'a' se o próximo passo falhar

    const b = try allocator.alloc(u8, 200);
    errdefer allocator.free(b);

    return Recurso{ .a = a, .b = b, .allocator = allocator };
    // Se chegamos aqui, os errdefers NÃO executam
}

// No chamador: use defer
pub fn main() !void {
    var r = try Recurso.init(allocator);
    defer r.deinit(); // Sempre executa ao sair do escopo
}
```

## Boas Práticas

- **Declare o `defer` imediatamente após adquirir o recurso**: Isso garante que não há caminho de saída que esqueça a limpeza, mesmo que código seja adicionado depois.
- **Implemente `deinit()` para structs com recursos**: Todo tipo que aloca memória, abre arquivos ou cria handles deve ter um método `deinit()` simétrico ao `init()`.
- **Prefira Arena para fluxos simples**: Se todos os recursos têm o mesmo tempo de vida, um `ArenaAllocator` com um único `defer arena.deinit()` é mais simples e menos propenso a erros.
- **Cuidado com `defer` em loops**: Um `defer` dentro de um `for` ou `while` executa ao final de cada iteração. Para cleanup por iteração isso é correto, mas para recursos que precisam sobreviver ao loop inteiro, declare o `defer` fora do loop.

## Armadilhas Comuns

- **Esquecer o defer**: Sem destructors automáticos, esquecer `defer deinit()` causa leak. O GPA detecta isso em modo debug.
- **defer em loops**: `defer` dentro de um loop executa ao fim da **iteração**, não ao fim do loop. Isso pode causar uso excessivo de memória se defers se acumularem.
- **Ordem de defer**: Defers executam em ordem reversa (LIFO). A ordem importa quando recursos dependem uns dos outros.
- **errdefer vs defer**: Use `errdefer` durante inicialização (antes de retornar com sucesso) e `defer` no chamador para limpeza normal.

## Termos Relacionados

- [Defer](/glossario/defer/) — Mecanismo de limpeza ao sair do escopo
- [Errdefer](/glossario/errdefer/) — Limpeza condicional em caso de erro
- [Allocator](/glossario/allocator/) — Gerenciamento de memória
- [Memory Leak](/glossario/memory-leak/) — O que acontece sem RAII

## Tutoriais Relacionados

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