---
title: "Structs, Enums e Unions em Zig: Guia Completo"
url: "https://ziglang.com.br/tutoriais/structs-enums-unions-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/structs-enums-unions-zig.MD"
description: "Structs, Enums e Unions em Zig com exemplos práticos. Guia completo de tipos compostos com comparação C vs Zig."
date: "2026-02-10"
author: ""
---

# Structs, Enums e Unions em Zig: Guia Completo

Structs, Enums e Unions em Zig com exemplos práticos. Guia completo de tipos compostos com comparação C vs Zig.


# Structs, Enums e Unions em Zig: Guia Completo

Structs, enums e unions são os **tipos compostos fundamentais** de Zig. Eles formam a base para modelar dados complexos e são essenciais para qualquer programa não-trivial. Se você vem de C, vai reconhecer esses conceitos — mas Zig adiciona recursos modernos que tornam o código mais seguro e expressivo.

Neste guia completo, você vai aprender tudo sobre tipos compostos em Zig: desde os conceitos básicos até padrões avançados usados em código de produção.

> **Pré-requisitos:** Conhecimento básico de Zig (variáveis, funções, tipos primitivos). Se você é novo no Zig, comece com nosso [guia de instalação](/tutoriais/como-instalar-zig/) e [introdução à linguagem](/tutoriais/o-que-e-zig/).

---

## Índice

1. [Introdução aos Tipos Compostos](#introdução-aos-tipos-compostos)
2. [Structs em Zig](#structs-em-zig)
3. [Enums em Zig](#enums-em-zig)
4. [Unions em Zig](#unions-em-zig)
5. [Tipos Especiais e Padrões](#tipos-especiais-e-padrões)
6. [Comparação com C](#comparação-com-c)
7. [Exemplos Práticos Completos](#exemplos-práticos-completos)
8. [Melhores Práticas](#melhores-práticas)
9. [FAQ](#faq)
10. [Próximos Passos](#próximos-passos)

---

## Introdução aos Tipos Compostos

Em Zig, tipos compostos permitem agrupar dados relacionados em uma única estrutura. Eles são **zero-cost abstractions** — não adicionam overhead em runtime comparado a acessar os dados manualmente.

### Os Três Tipos Compostos

| Tipo | Uso Principal | Analogia em C |
|------|---------------|---------------|
| **Struct** | Agrupar campos relacionados | `struct` |
| **Enum** | Definir conjunto de valores nomeados | `enum` |
| **Union** | Armazenar diferentes tipos no mesmo espaço | `union` |

### Por Que São Importantes?

```zig
// ❌ Sem struct: dados desorganizados
const nome = "Alice";
const idade = 30;
const email = "alice@email.com";

// ✅ Com struct: dados coesos
const Usuario = struct {
    nome: []const u8,
    idade: u32,
    email: []const u8,
};
```

Structs, enums e unions trabalham juntos para criar modelos de dados robustos e type-safe.

---

## Structs em Zig

Structs são coleções nomeadas de campos. São o tipo composto mais comum em Zig.

### Declaração Básica

```zig
const Ponto = struct {
    x: f64,
    y: f64,
};

const Retangulo = struct {
    origem: Ponto,
    largura: f64,
    altura: f64,
};
```

Structs podem conter:
- Campos de qualquer tipo (incluindo outros structs)
- Métodos (funções associadas)
- Declarações `const` e `var`

### Inicialização

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

pub fn main() void {
    // Inicialização com valores nomeados (recomendado)
    const p1 = Ponto{
        .x = 10.5,
        .y = 20.3,
    };
    
    // Inicialização com valores posicionais
    const p2 = Ponto{ 5.0, 8.0 };
    
    // Inicialização parcial com valores padrão (comptime)
    const Retangulo = struct {
        origem: Ponto = Ponto{ .x = 0, .y = 0 },
        largura: f64,
        altura: f64,
    };
    
    const ret = Retangulo{
        .largura = 100,
        .altura = 50,
        // origem usa valor padrão
    };
    
    std.debug.print("Ponto 1: ({d}, {d})\n", .{ p1.x, p1.y });
}
```

### Acesso a Campos

```zig
const p = Ponto{ .x = 10, .y = 20 };

// Acesso direto
const x = p.x;
const y = p.y;

// Modificação (requer variável mutável)
var p_mut = Ponto{ .x = 0, .y = 0 };
p_mut.x = 15;
p_mut.y = 25;
```

### Métodos em Structs

Structs podem ter métodos — funções que operam na struct:

```zig
const Circulo = struct {
    centro: Ponto,
    raio: f64,
    
    // Método que recebe self por valor (imutável)
    pub fn area(self: Circulo) f64 {
        return std.math.pi * self.raio * self.raio;
    }
    
    // Método que recebe self por referência mutável
    pub fn escalar(self: *Circulo, fator: f64) void {
        self.raio *= fator;
    }
    
    // Método estático (não recebe self)
    pub fn unitario() Circulo {
        return Circulo{
            .centro = Ponto{ .x = 0, .y = 0 },
            .raio = 1.0,
        };
    }
};

pub fn main() void {
    var c = Circulo{
        .centro = Ponto{ .x = 0, .y = 0 },
        .raio = 5.0,
    };
    
    // Chamada de método
    std.debug.print("Área: {d}\n", .{c.area()});
    
    // Modificação via método
    c.escalar(2.0);
    std.debug.print("Novo raio: {d}\n", .{c.raio});
    
    // Método estático
    const unit = Circulo.unitario();
}
```

### Self em Métodos

Zig não tem `self` implícito como Python. Você declara explicitamente:

```zig
const MeuStruct = struct {
    valor: i32,
    
    // self por valor (cópia)
    pub fn getValorCopia(self: MeuStruct) i32 {
        return self.valor;
    }
    
    // self por referência constante
    pub fn getValorRef(self: *const MeuStruct) i32 {
        return self.valor;
    }
    
    // self por referência mutável
    pub fn setValor(self: *MeuStruct, novo: i32) void {
        self.valor = novo;
    }
};
```

**Recomendação:** Use `*const Self` para métodos de leitura, `*Self` para métodos de modificação.

### Structs Anônimos

Structs podem ser declarados sem nome (anônimos):

```zig
// Struct anônimo como tipo de retorno
fn criarPessoa(nome: []const u8, idade: u32) struct { nome: []const u8, idade: u32 } {
    return .{ .nome = nome, .idade = idade };
}

// Uso
const pessoa = criarPessoa("Alice", 30);
```

Structs anônimos são úteis para tipos temporários ou retornos de funções.

### Structs com `packed`

Structs packed garantem layout específico na memória (útil para binary protocols):

```zig
const HeaderPacote = packed struct {
    versao: u4,
    flags: u4,
    tamanho: u16,
    checksum: u32,
};

// Tamanho garantido: exatamente 8 bytes
comptime {
    std.debug.assert(@sizeOf(HeaderPacote) == 8);
}
```

**Atenção:** Structs packed têm restrições — campos não podem ser ponteiros para `self`.

### Structs com `extern`

Structs extern têm layout compatível com C:

```zig
const CoordenadaC = extern struct {
    x: c_int,
    y: c_int,
};

// Pode ser passado diretamente para funções C
```

---

## Enums em Zig

Enums definem um tipo que pode ter um de vários valores nomeados.

### Declaração Básica

```zig
const StatusPedido = enum {
    pendente,
    processando,
    enviado,
    entregue,
    cancelado,
};

const Cor = enum {
    vermelho,
    verde,
    azul,
};
```

Por padrão, enums em Zig usam `usize` como tipo subjacente (0, 1, 2, ...).

### Enums com Tipo Específico

```zig
// Enum com tipo subjacente específico
const CodigoErro = enum(u8) {
    sucesso = 0,
    arquivo_nao_encontrado = 1,
    permissao_negada = 2,
    memoria_insuficiente = 3,
    timeout = 4,
};

// Tamanho explícito
comptime {
    std.debug.assert(@sizeOf(CodigoErro) == 1); // u8 = 1 byte
}
```

### Valores Personalizados

```zig
const FlagsPermissao = enum(u8) {
    nenhuma = 0,
    leitura = 1 << 0,    // 1
    escrita = 1 << 1,    // 2
    execucao = 1 << 2,   // 4
    todas = 0b111,       // 7
};
```

### Métodos em Enums

Enums também podem ter métodos:

```zig
const Status = enum {
    ativo,
    inativo,
    suspenso,
    
    pub fn podeAcessar(self: Status) bool {
        return self == .ativo;
    }
    
    pub fn descricao(self: Status) []const u8 {
        return switch (self) {
            .ativo => "Usuário ativo",
            .inativo => "Usuário inativo",
            .suspenso => "Usuário suspenso",
        };
    }
};

pub fn main() void {
    const status = Status.ativo;
    std.debug.print("Pode acessar? {s}\n", .{if (status.podeAcessar()) "sim" else "não"});
    std.debug.print("Descrição: {s}\n", .{status.descricao()});
}
```

### Conversão para/de Inteiro

```zig
const codigo = @intFromEnum(CodigoErro.permissao_negada); // 2
const erro = @enumFromInt(CodigoErro, 2);                 // .permissao_negada
```

### `std.meta.Tag`

Para obter o tipo subjacente de um enum:

```zig
const TipoSubjacente = @typeInfo(Status).Enum.tag_type; // usize
```

---

## Unions em Zig

Unions permitem armazenar diferentes tipos no mesmo espaço de memória. Zig oferece dois tipos: untagged (inseguro) e tagged (seguro).

### Union Untagged (Inseguro)

Similar a unions em C — você é responsável por rastrear qual campo está ativo:

```zig
const Dado = union {
    inteiro: i32,
    flutuante: f64,
    texto: []const u8,
};

pub fn main() void {
    var dado: Dado = undefined;
    
    // Armazena inteiro
    dado.inteiro = 42;
    std.debug.print("Inteiro: {d}\n", .{dado.inteiro});
    
    // Agora armazena float (sobrescreve inteiro)
    dado.flutuante = 3.14;
    std.debug.print("Float: {d}\n", .{dado.flutuante});
    
    // ⚠️ Acessar campo errado é comportamento indefinido!
    // std.debug.print("{d}", .{dado.inteiro}); // PERIGOSO!
}
```

### Union Tagged (Seguro)

Tagged unions combinam uma union com um enum — o enum indica qual campo está ativo:

```zig
const Valor = union(enum) {
    inteiro: i32,
    flutuante: f64,
    texto: []const u8,
    nulo,
};

pub fn main() void {
    const v1 = Valor{ .inteiro = 42 };
    const v2 = Valor{ .flutuante = 3.14 };
    const v3 = Valor{ .texto = "Olá" };
    const v4 = Valor.nulo;
    
    // Switch seguro — o compilador garante que todos os casos são tratados
    switch (v1) {
        .inteiro => |i| std.debug.print("Inteiro: {d}\n", .{i}),
        .flutuante => |f| std.debug.print("Float: {d}\n", .{f}),
        .texto => |t| std.debug.print("Texto: {s}\n", .{t}),
        .nulo => std.debug.print("Nulo\n"),
    }
}
```

A sintaxe `union(enum)` cria automaticamente um enum implícito. Você também pode usar um enum explícito:

```zig
const TipoValor = enum {
    inteiro,
    flutuante,
    texto,
};

const ValorExplicito = union(TipoValor) {
    inteiro: i32,
    flutuante: f64,
    texto: []const u8,
};
```

### Captura por Referência

No switch, você pode capturar o valor por referência para modificação:

```zig
var valor = Valor{ .inteiro = 10 };

switch (valor) {
    .inteiro => |*i| i.* += 1,  // Modifica o valor
    else => {},
}

std.debug.print("Valor: {d}\n", .{valor.inteiro}); // 11
```

### Union com `packed` e `extern`

```zig
// Layout compatível com C
const DadoC = extern union {
    inteiro: c_int,
    flutuante: f32,
};

// Layout packed
const DadoPacked = packed union {
    bits: u32,
    float: f32,
};
```

---

## Tipos Especiais e Padrões

### Optional Types (`?T`)

Optional é um tipo built-in que representa "valor ou nulo":

```zig
// ?i32 pode ser i32 ou null
const talvezNumero: ?i32 = 42;
const talvezNulo: ?i32 = null;

// Desempacotamento seguro
if (talvezNumero) |numero| {
    std.debug.print("Valor: {d}\n", .{numero});
} else {
    std.debug.print("É nulo\n");
}

// Operador orelse (valor padrão)
const valor = talvezNulo orelse 0; // 0

// Orelse com retorno
const valorOuErro = talvezNulo orelse return error.ValorNulo;
```

### Error Union (`!T`)

Error union representa "valor ou erro":

```zig
// !i32 pode ser i32 ou um erro
fn dividir(a: i32, b: i32) !i32 {
    if (b == 0) return error.DivisaPorZero;
    return @divTrunc(a, b);
}

// Try propaga erros
const resultado = try dividir(10, 2);

// Catch trata erros
const seguro = dividir(10, 0) catch |err| {
    std.debug.print("Erro: {}\n", .{err});
    0 // valor padrão
};
```

### Anyerror

`anyerror` é o tipo de todos os erros:

```zig
const MeuErro = error{ NaoEncontrado, Timeout };
const OutroErro = error{ PermissaoNegada };

// anyerror pode ser qualquer erro
fn podeFalhar() anyerror!void {
    return error.NaoEncontrado;
}
```

### Error Sets

Conjuntos de erros podem ser combinados:

```zig
const ErroIO = error{ ArquivoNaoEncontrado, PermissaoNegada };
const ErroRede = error{ Timeout, ConexaoRecusada };

// União de erros
const ErroApp = ErroIO || ErroRede;

fn operacaoComplexa() ErroApp!void {
    // Pode retornar qualquer erro de ErroIO ou ErroRede
}
```

### Type Inference com `.`

Zig pode inferir o tipo em muitos contextos:

```zig
const Status = enum { ativo, inativo };

// Inferido como Status.ativo
const s: Status = .ativo;

const Valor = union(enum) { numero: i32, texto: []const u8 };

// Inferido como Valor{ .numero = 42 }
const v: Valor = .{ .numero = 42 };
```

---

## Comparação com C

| Aspecto | Zig | C |
|---------|-----|---|
| **Struct** | `struct { x: i32 }` | `struct { int x; }` |
| **Enum** | `enum { a, b }` com tipo opcional | Sempre `int` |
| **Union** | `union { x: i32 }` | `union { int x; }` |
| **Tagged Union** | Nativo com `union(enum)` | Não existe |
| **Optional** | `?T` built-in | Requer ponteiros |
| **Error handling** | `!T` built-in | Códigos de erro |
| **Métodos** | Nativo em structs/enums | Requer funções separadas |
| **Type safety** | Strong | Weak (casts implícitos) |

### Exemplo Comparativo

**C:**
```c
// Struct simples
struct Ponto {
    double x;
    double y;
};

// Enum
enum Status { ATIVO, INATIVO };

// Union insegura
union Dado {
    int i;
    double f;
};

// Sem tagged unions nativamente
// Sem optional types
// Sem error unions
```

**Zig:**
```zig
// Struct com métodos
const Ponto = struct {
    x: f64,
    y: f64,
    
    pub fn distancia(self: Ponto, outro: Ponto) f64 {
        const dx = self.x - outro.x;
        const dy = self.y - outro.y;
        return std.math.sqrt(dx * dx + dy * dy);
    }
};

// Enum com tipo específico
const Status = enum(u8) { ativo, inativo };

// Union tagged (segura)
const Dado = union(enum) {
    inteiro: i32,
    flutuante: f64,
};

// Optional e error unions nativos
const talvezValor: ?i32 = null;
const resultado: !i32 = calcular();
```

---

## Exemplos Práticos Completos

### Exemplo 1: Sistema de Configuração

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

const Config = struct {
    nome: []const u8,
    versao: []const u8,
    debug: bool = false,
    portas: struct {
        http: u16 = 8080,
        https: u16 = 8443,
    } = .{},
};

const Ambiente = enum {
    desenvolvimento,
    homologacao,
    producao,
};

fn criarConfig(ambiente: Ambiente) Config {
    return switch (ambiente) {
        .desenvolvimento => Config{
            .nome = "MeuApp",
            .versao = "dev",
            .debug = true,
            .portas = .{ .http = 3000, .https = 3443 },
        },
        .homologacao => Config{
            .nome = "MeuApp",
            .versao = "beta",
            .debug = true,
        },
        .producao => Config{
            .nome = "MeuApp",
            .versao = "1.0.0",
            .debug = false,
        },
    };
}

pub fn main() void {
    const config = criarConfig(.desenvolvimento);
    std.debug.print("App: {s} v{s}\n", .{ config.nome, config.versao });
    std.debug.print("Porta HTTP: {d}\n", .{config.portas.http});
}
```

### Exemplo 2: AST (Abstract Syntax Tree)

```zig
const Expr = union(enum) {
    numero: i64,
    variavel: []const u8,
    binaria: struct {
        op: Operador,
        esq: *const Expr,
        dir: *const Expr,
    },
    chamada: struct {
        funcao: []const u8,
        args: []const Expr,
    },
    
    const Operador = enum {
        soma,
        subtracao,
        multiplicacao,
        divisao,
    };
    
    pub fn avaliar(self: Expr) !i64 {
        return switch (self) {
            .numero => |n| n,
            .variavel => error.VariavelNaoSuportada,
            .binaria => |b| {
                const esq = try b.esq.avaliar();
                const dir = try b.dir.avaliar();
                return switch (b.op) {
                    .soma => esq + dir,
                    .subtracao => esq - dir,
                    .multiplicacao => esq * dir,
                    .divisao => @divTrunc(esq, dir),
                };
            },
            .chamada => error.ChamadaNaoSuportada,
        };
    }
};

pub fn main() !void {
    const dois = Expr{ .numero = 2 };
    const tres = Expr{ .numero = 3 };
    
    const soma = Expr{
        .binaria = .{
            .op = .soma,
            .esq = &dois,
            .dir = &tres,
        },
    };
    
    const resultado = try soma.avaliar();
    std.debug.print("2 + 3 = {d}\n", .{resultado});
}
```

### Exemplo 3: Resultado de Operação

```zig
const Resultado = union(enum) {
    sucesso: []const u8,
    erro: Erro,
    
    const Erro = struct {
        codigo: u32,
        mensagem: []const u8,
    };
    
    pub fn estaOk(self: Resultado) bool {
        return self == .sucesso;
    }
    
    pub fn getMensagem(self: Resultado) []const u8 {
        return switch (self) {
            .sucesso => |s| s,
            .erro => |e| e.mensagem,
        };
    }
};

fn operacaoArquivos() Resultado {
    // Simula operação
    const sucesso = false;
    
    if (sucesso) {
        return .{ .sucesso = "Arquivo processado com sucesso" };
    } else {
        return .{ .erro = .{
            .codigo = 404,
            .mensagem = "Arquivo não encontrado",
        } };
    }
}

pub fn main() void {
    const resultado = operacaoArquivos();
    
    if (resultado.estaOk()) {
        std.debug.print("✅ {s}\n", .{resultado.getMensagem()});
    } else {
        std.debug.print("❌ Erro: {s}\n", .{resultado.getMensagem()});
    }
}
```

---

## Melhores Práticas

### 1. Use Structs para Agrupar Dados Relacionados

```zig
// ✅ Bom
const Usuario = struct {
    id: u64,
    nome: []const u8,
    email: []const u8,
    criado_em: i64,
};

// ❌ Ruim: dados soltos
const usuario_id: u64 = 1;
const usuario_nome: []const u8 = "Alice";
const usuario_email: []const u8 = "alice@email.com";
```

### 2. Prefira Tagged Unions

```zig
// ✅ Seguro: tagged union
const Resultado = union(enum) {
    sucesso: Dados,
    erro: MensagemErro,
};

// ❌ Inseguro: untagged union (use apenas quando necessário)
const ResultadoInseguro = union {
    sucesso: Dados,
    erro: MensagemErro,
};
```

### 3. Use Valores Padrão

```zig
const Config = struct {
    timeout_ms: u32 = 5000,      // valor padrão
    max_retries: u32 = 3,        // valor padrão
    log_level: LogLevel = .info, // valor padrão
};

// Inicialização simples
const config = Config{ .timeout_ms = 10000 };
// max_retries e log_level usam defaults
```

### 4. Implemente Métodos Úteis

```zig
const Vetor2D = struct {
    x: f64,
    y: f64,
    
    // Construtores
    pub fn zero() Vetor2D {
        return .{ .x = 0, .y = 0 };
    }
    
    pub fn new(x: f64, y: f64) Vetor2D {
        return .{ .x = x, .y = y };
    }
    
    // Operações
    pub fn add(self: Vetor2D, outro: Vetor2D) Vetor2D {
        return .{
            .x = self.x + outro.x,
            .y = self.y + outro.y,
        };
    }
    
    pub fn magnitude(self: Vetor2D) f64 {
        return std.math.sqrt(self.x * self.x + self.y * self.y);
    }
};
```

### 5. Documente com Comentários

```zig
/// Representa um usuário do sistema.
/// Campos obrigatórios: id, nome
/// Campos opcionais: avatar, bio
const Usuario = struct {
    id: u64,                // ID único do usuário
    nome: []const u8,       // Nome de exibição
    email: []const u8,      // Email para login
    ativo: bool = true,     // Status da conta
};
```

### 6. Use `extern` para Interoperabilidade com C

```zig
// Sempre use extern struct quando for passar para C
const PontoC = extern struct {
    x: f64,
    y: f64,
};

// Função exportada para C
export fn calcularDistancia(a: PontoC, b: PontoC) f64 {
    // ...
}
```

---

## FAQ

### Qual a diferença entre struct e union?

**Struct:** Todos os campos existem simultaneamente. Tamanho = soma dos campos.

**Union:** Apenas um campo existe por vez. Tamanho = maior campo.

```zig
const S = struct { a: i32, b: f64 };  // 16 bytes
const U = union  { a: i32, b: f64 };  // 8 bytes
```

### Como criar um construtor em Zig?

Structs não têm construtores especiais. Use funções estáticas:

```zig
const Ponto = struct {
    x: f64,
    y: f64,
    
    pub fn new(x: f64, y: f64) Ponto {
        return .{ .x = x, .y = y };
    }
};

const p = Ponto.new(10, 20);
```

### Posso ter herança em Zig?

Não. Zig não tem herança de classes. Use **composição**:

```zig
const Animal = struct {
    nome: []const u8,
};

const Cachorro = struct {
    animal: Animal,  // composição
    raca: []const u8,
};
```

### Como fazer pattern matching em enums?

Use `switch`:

```zig
const status = Status.ativo;

const mensagem = switch (status) {
    .ativo => "Online",
    .inativo => "Offline",
};
```

### Union tagged vs optional — quando usar cada?

- **Optional (`?T`)**: "tem valor ou não tem"
- **Union tagged**: "tem valor do tipo A, OU tipo B, OU tipo C"

```zig
const talvezNumero: ?i64 = null;              // simples
const valor: union(enum) { i: i64, f: f64 };  // múltiplos tipos
```

### Como verificar o tipo atual de uma tagged union?

```zig
const valor = Valor{ .inteiro = 42 };

if (valor == .inteiro) {
    std.debug.print("É um inteiro: {d}\n", .{valor.inteiro});
}
```

### Posso modificar uma tagged union?

Sim, mas precisa ser mutável:

```zig
var valor = Valor{ .inteiro = 10 };
valor = Valor{ .flutuante = 3.14 };  // OK
```

---

## Próximos Passos

Agora que você domina structs, enums e unions em Zig, continue seu aprendizado:

### Conteúdo Relacionado

1. **[Gerenciamento de Memória em Zig](/tutoriais/gerenciamento-de-memoria-zig/)** — Aprofunde em allocators e como structs são alocadas
2. **[Comptime em Zig](/tutoriais/comptime-em-zig/)** — Aprenda metaprogramação para gerar structs dinamicamente
3. **[Strings e Arrays em Zig](/tutoriais/strings-e-arrays-zig/)** — Fundamentos de coleções de dados
4. **[Tratamento de Erros em Zig](/tutoriais/tratamento-de-erros-em-zig/)** — Domine error unions e error sets
5. **[Zig para Programadores C](/tutoriais/zig-para-programadores-c/)** — Compare structs/unions com C

### Pratique

- Implemente uma árvore binária usando structs e tagged unions
- Crie um parser simples usando tagged unions para tipos de tokens
- Modele um sistema de estados (máquina de estados) com enums

### Recursos Adicionais

- [Zig Guide - Structs](https://zig.guide/standard-library/structs/)
- [Zighelp - Types](https://zighelp.org/chapter-1/)
- [Documentação Oficial - Types](https://ziglang.org/documentation/master/#Types)

---

## Resumo

| Conceito | Uso Principal | Exemplo |
|----------|---------------|---------|
| **Struct** | Agrupar dados relacionados | `Ponto{ .x = 1, .y = 2 }` |
| **Enum** | Conjunto de valores nomeados | `Status.ativo` |
| **Union** | Diferentes tipos no mesmo espaço | `Dado{ .inteiro = 42 }` |
| **Tagged Union** | Union segura com discriminação | `union(enum) { a: i32 }` |
| **Optional** | Valor ou nulo | `?i32` |
| **Error Union** | Valor ou erro | `!i32` |

Structs, enums e unions são os blocos de construção para modelar dados em Zig. Combinados com optional types e error unions, eles formam um sistema de tipos expressivo e type-safe que ajuda a prevenir bugs em tempo de compilação.

---

*Escrito por [Camila](/sobre/) para [ZigLang Brasil](/). Última atualização: 10 de fevereiro de 2026.*

*Achou algum erro? Tem sugestões? [Contribua no GitHub](https://github.com/seu-repo/ziglangbr)*
