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