Union em Zig — O que é e Como Usar

Union em Zig — O que é e Como Usar

Definição

Uma union em Zig é um tipo que pode armazenar um único valor dentre vários campos possíveis, todos compartilhando o mesmo espaço de memória. O tamanho da union é igual ao tamanho do seu maior campo. Existem três variantes de unions em Zig:

  • union(enum): Tagged union — segura, com tag automática (ver Tagged Union)
  • extern union: Compatível com layout C, sem tag
  • union (bare): Sem tag, com verificação em modo Debug

A forma mais comum e recomendada é a tagged union (union(enum)). Bare unions e extern unions existem para interoperabilidade e otimizações específicas.

Por que Unions Importam

  1. Economia de memória: Todos os campos compartilham o mesmo espaço — útil quando só um estará ativo por vez.
  2. Interoperabilidade com C: extern union permite usar unions de APIs C.
  3. Modelagem de tipos variantes: Representam dados que podem ter formas diferentes.
  4. Pattern matching: Tagged unions combinam com switch para despacho seguro.

Exemplo Prático

Bare Union (Sem Tag)

const std = @import("std");

const Valor = union {
    inteiro: i64,
    float: f64,
    ponteiro: *anyopaque,
};

pub fn main() void {
    var v = Valor{ .inteiro = 42 };

    // Em modo Debug, acessar o campo errado causa panic
    std.debug.print("Inteiro: {}\n", .{v.inteiro});

    // Reatribuir para outro campo
    v = Valor{ .float = 3.14 };
    std.debug.print("Float: {d}\n", .{v.float});
}

Extern Union (Compatível com C)

const c = @cImport(@cInclude("sys/socket.h"));

// Representa a union sockaddr_in do C
const EnderecoIP = extern union {
    v4: [4]u8,
    v4_int: u32,
};

pub fn main() void {
    const addr = EnderecoIP{ .v4 = .{ 192, 168, 1, 1 } };
    // Acessar como inteiro (reinterpretação de bits)
    std.debug.print("Como u32: {}\n", .{addr.v4_int});
}

Tagged Union (Recomendada)

const Resultado = union(enum) {
    sucesso: i32,
    erro_io: std.posix.E,
    erro_parse: struct { linha: u32, coluna: u32 },
    nenhum: void,

    pub fn ehSucesso(self: Resultado) bool {
        return self == .sucesso;
    }
};

fn processar() Resultado {
    return .{ .sucesso = 42 };
}

pub fn main() void {
    const r = processar();
    switch (r) {
        .sucesso => |val| std.debug.print("OK: {}\n", .{val}),
        .erro_io => |e| std.debug.print("Erro IO: {}\n", .{e}),
        .erro_parse => |e| std.debug.print("Parse erro: {}:{}\n", .{ e.linha, e.coluna }),
        .nenhum => std.debug.print("Nada\n", .{}),
    }
}

Comparação de Tipos de Union

TipoTagSegurançaUso
union(enum)AutomáticaTotalUso geral
union (bare)NenhumaDebug onlyOtimização
extern unionNenhumaNenhumaInterop C

Armadilhas Comuns

  • Acessar campo inativo em bare union: Em modo Release, não há verificação — comportamento indefinido. Prefira tagged unions.
  • Esquecer de usar union(enum): Se você não precisa de interop com C, sempre use tagged unions para segurança.
  • Assumir layout: O layout de union (bare) não é garantido. Use extern union se precisar de layout compatível com C.
  • Tamanho inesperado: O tamanho é o do maior campo + padding. Uma tagged union também inclui o tamanho da tag enum.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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