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 tagunion(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
- Economia de memória: Todos os campos compartilham o mesmo espaço — útil quando só um estará ativo por vez.
- Interoperabilidade com C:
extern unionpermite usar unions de APIs C. - Modelagem de tipos variantes: Representam dados que podem ter formas diferentes.
- Pattern matching: Tagged unions combinam com
switchpara 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
| Tipo | Tag | Segurança | Uso |
|---|---|---|---|
union(enum) | Automática | Total | Uso geral |
union (bare) | Nenhuma | Debug only | Otimização |
extern union | Nenhuma | Nenhuma | Interop C |
Quando Usar Cada Tipo de Union
union(enum): Na grande maioria dos casos. Oferece segurança total com switch exhaustivo e verificação de campo ativo em runtime.extern union: Apenas ao fazer interop com structs C que contém unions. O layout deve ser exatamente o mesmo definido pelo C ABI.union(bare): Raramente. Pode ser útil em otimizações muito específicas onde você garante externamente qual campo está ativo, mas o custo de bugs é alto.
Casos de Uso Reais
Tagged unions são excelentes para modelar resultados com contexto variável:
const Evento = union(enum) {
tecla_pressionada: u8,
mouse_movido: struct { x: i32, y: i32 },
redimensionar: struct { largura: u32, altura: u32 },
fechar: void,
};
fn processarEvento(ev: Evento) void {
switch (ev) {
.tecla_pressionada => |key| {
std.debug.print("Tecla: {c}\n", .{key});
},
.mouse_movido => |pos| {
std.debug.print("Mouse: {}x{}\n", .{ pos.x, pos.y });
},
.redimensionar => |dim| {
std.debug.print("Novo tamanho: {}x{}\n", .{ dim.largura, dim.altura });
},
.fechar => std.debug.print("Fechando\n", .{}),
}
}
Neste exemplo, o compilador garante que todos os casos são tratados. Adicionar um novo campo à union sem atualizar o switch resulta em erro de compilação.
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. Useextern unionse 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
- Tagged Union — Union com discriminante automático
- Enum — Enumerações usadas como tags
- Struct — Tipos estruturados
- Packed Struct — Structs com layout exato