Cheatsheet: Enums e Unions em Zig

Cheatsheet: Enums e Unions em Zig

Referência completa para enumerações e unions em Zig, incluindo tagged unions, pattern matching e padrões de uso comuns.

Enums Básicos

// Definição simples
const Cor = enum {
    vermelho,
    verde,
    azul,
};

const minha_cor: Cor = .verde;

// Comparação
if (minha_cor == .verde) {
    std.debug.print("É verde!\n", .{});
}

Enums com Tipo Inteiro

// Enum com tipo subjacente
const HttpStatus = enum(u16) {
    ok = 200,
    created = 201,
    bad_request = 400,
    not_found = 404,
    internal_error = 500,
};

// Converter enum para inteiro
const codigo: u16 = @intFromEnum(HttpStatus.not_found);  // 404

// Converter inteiro para enum
const status: HttpStatus = @enumFromInt(200);  // .ok

Enums com Métodos

const Direcao = enum {
    norte,
    sul,
    leste,
    oeste,

    const Self = @This();

    pub fn oposta(self: Self) Self {
        return switch (self) {
            .norte => .sul,
            .sul => .norte,
            .leste => .oeste,
            .oeste => .leste,
        };
    }

    pub fn toString(self: Self) []const u8 {
        return switch (self) {
            .norte => "Norte",
            .sul => "Sul",
            .leste => "Leste",
            .oeste => "Oeste",
        };
    }

    pub fn eHorizontal(self: Self) bool {
        return self == .leste or self == .oeste;
    }
};

// Uso
const dir = Direcao.norte;
const op = dir.oposta();     // .sul
const nome = dir.toString();  // "Norte"
_ = op;
_ = nome;

Enum com Namespace

const Comando = enum {
    iniciar,
    parar,
    pausar,

    // Constantes associadas
    pub const TODOS = [_]Comando{ .iniciar, .parar, .pausar };

    // Função utilitária
    pub fn parse(texto: []const u8) ?Comando {
        const mapa = std.StaticStringMap(Comando).initComptime(.{
            .{ "iniciar", .iniciar },
            .{ "parar", .parar },
            .{ "pausar", .pausar },
        });
        return mapa.get(texto);
    }
};

Iteração sobre Enums

const Naipe = enum { copas, ouros, paus, espadas };

// Iterar sobre todos os valores
for (std.enums.values(Naipe)) |naipe| {
    std.debug.print("Naipe: {}\n", .{naipe});
}

// Contar valores
const total = std.enums.values(Naipe).len;  // 4

// EnumSet
var set = std.EnumSet(Naipe).initEmpty();
set.insert(.copas);
set.insert(.espadas);
const tem_copas = set.contains(.copas);  // true
_ = total;
_ = tem_copas;

Tagged Unions

// Tagged union — combina enum e union
const Forma = union(enum) {
    circulo: f64,              // raio
    retangulo: struct { largura: f64, altura: f64 },
    triangulo: struct { base: f64, altura: f64 },

    pub fn area(self: Forma) f64 {
        return switch (self) {
            .circulo => |r| std.math.pi * r * r,
            .retangulo => |rect| rect.largura * rect.altura,
            .triangulo => |tri| tri.base * tri.altura / 2.0,
        };
    }
};

// Criação
const c = Forma{ .circulo = 5.0 };
const r = Forma{ .retangulo = .{ .largura = 4.0, .altura = 3.0 } };

// Uso
const area_c = c.area();  // ~78.54
const area_r = r.area();  // 12.0
_ = area_c;
_ = area_r;

Pattern Matching com Switch

const Token = union(enum) {
    numero: f64,
    operador: u8,
    parentese_abre: void,
    parentese_fecha: void,
    fim: void,
};

fn processar(token: Token) void {
    switch (token) {
        .numero => |n| {
            std.debug.print("Número: {d}\n", .{n});
        },
        .operador => |op| {
            std.debug.print("Operador: {c}\n", .{op});
        },
        .parentese_abre, .parentese_fecha => {
            std.debug.print("Parêntese\n", .{});
        },
        .fim => {
            std.debug.print("Fim\n", .{});
        },
    }
}

// Switch exaustivo — o compilador exige que todos os casos sejam cobertos

Unions sem Tag

// Union sem tag — acesso direto (unsafe)
const Registro = union {
    inteiro: i64,
    float: f64,
    bytes: [8]u8,
};

// Útil para reinterpretar bits
var reg = Registro{ .float = 3.14 };
// Acessar como bytes é comportamento indefinido sem tag
// Use @bitCast em vez disso para reinterpretação segura

Extern Unions

// Union compatível com C
const CUnion = extern union {
    i: c_int,
    f: f32,
    ptr: ?*anyopaque,
};

// Layout de memória idêntico ao C

Padrões com Optionals e Errors

// Optional como tagged union implícita
// ?T é essencialmente union(enum) { value: T, null: void }
var opt: ?i32 = 42;

// Desempacotar
if (opt) |valor| {
    std.debug.print("Valor: {}\n", .{valor});
}

// Error union como tagged union implícita
// E!T é essencialmente union { value: T, err: E }
const resultado: anyerror!i32 = 42;

if (resultado) |valor| {
    std.debug.print("Ok: {}\n", .{valor});
} else |err| {
    std.debug.print("Erro: {}\n", .{err});
}

Padrão Tipo Soma (Sum Type)

// Representar JSON
const JsonValue = union(enum) {
    null: void,
    boolean: bool,
    integer: i64,
    float: f64,
    string: []const u8,
    array: []JsonValue,
    object: std.StringHashMap(JsonValue),

    pub fn isNull(self: JsonValue) bool {
        return self == .null;
    }

    pub fn asString(self: JsonValue) ?[]const u8 {
        return switch (self) {
            .string => |s| s,
            else => null,
        };
    }
};

// Representar AST
const Expr = union(enum) {
    literal: f64,
    variavel: []const u8,
    binario: struct {
        op: u8,
        esq: *const Expr,
        dir: *const Expr,
    },
    unario: struct {
        op: u8,
        operando: *const Expr,
    },
};

Conversões e Coerções

const Animal = union(enum) {
    gato: struct { nome: []const u8 },
    cachorro: struct { nome: []const u8, raca: []const u8 },
};

// Verificar variante ativa
const pet = Animal{ .gato = .{ .nome = "Mimi" } };
const eh_gato = (pet == .gato);  // true
_ = eh_gato;

// Obter tag como enum
const tag = std.meta.activeTag(pet);  // .gato
_ = tag;

// Enum do tagged union
const AnimalTag = std.meta.Tag(Animal);
const t: AnimalTag = .gato;
_ = t;

Tabela de Referência

OperaçãoSintaxe
Definir enumconst E = enum { a, b, c };
Enum tipadoconst E = enum(u8) { a = 1, b = 2 };
Valor enum.variante
Enum → int@intFromEnum(e)
Int → enum@enumFromInt(n)
Tagged unionconst U = union(enum) { ... };
Criar unionU{ .campo = valor }
Switch unionswitch (u) { .a => |v| ... }
Verificar tagu == .variante
Tag ativastd.meta.activeTag(u)
Iterar enumstd.enums.values(E)

Veja Também

Continue aprendendo Zig

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