Struct em Zig — O que é e Como Usar
Definição
Uma struct (estrutura) em Zig é um tipo composto que agrupa campos nomeados de diferentes tipos em uma única unidade. Structs são o bloco fundamental para organização de dados em Zig — desempenham o papel que classes têm em linguagens orientadas a objeto, mas sem herança. Em vez disso, Zig favorece composição e interfaces via comptime.
Por que Structs Importam
- Organização de dados: Agrupam dados relacionados com nomes significativos.
- Métodos: Podem conter funções associadas, encapsulando comportamento.
- Valores default: Campos podem ter valores padrão, simplificando a criação.
- Genéricos: Structs podem ser geradas em comptime, criando tipos genéricos.
Exemplo Prático
Struct Básica
const std = @import("std");
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 @sqrt(dx * dx + dy * dy);
}
pub fn origem() Ponto {
return .{ .x = 0, .y = 0 };
}
};
pub fn main() void {
const a = Ponto{ .x = 3.0, .y = 4.0 };
const b = Ponto.origem();
std.debug.print("Distância: {d:.2}\n", .{a.distancia(b)}); // 5.00
}
Valores Default
const Config = struct {
porta: u16 = 8080,
host: []const u8 = "localhost",
max_conexoes: u32 = 100,
debug: bool = false,
};
pub fn main() void {
// Apenas os campos que diferem do padrão
const config = Config{
.porta = 3000,
.debug = true,
};
std.debug.print("Porta: {}, Debug: {}\n", .{ config.porta, config.debug });
}
Struct Genérica com Comptime
fn Pilha(comptime T: type) type {
return struct {
items: []T,
topo: usize,
allocator: std.mem.Allocator,
const Self = @This();
pub fn init(allocator: std.mem.Allocator, capacidade: usize) !Self {
return Self{
.items = try allocator.alloc(T, capacidade),
.topo = 0,
.allocator = allocator,
};
}
pub fn deinit(self: *Self) void {
self.allocator.free(self.items);
}
pub fn push(self: *Self, valor: T) !void {
if (self.topo >= self.items.len) return error.PilhaCheia;
self.items[self.topo] = valor;
self.topo += 1;
}
pub fn pop(self: *Self) ?T {
if (self.topo == 0) return null;
self.topo -= 1;
return self.items[self.topo];
}
};
}
Struct Anônima e Tupla
// Struct anônima (usada como literal)
const ponto = .{ .x = 10, .y = 20 };
// Tupla (struct anônima com campos indexados)
const tupla = .{ @as(u32, 42), "texto", true };
std.debug.print("Valor: {}\n", .{tupla[0]}); // 42
Tipos de Struct
| Tipo | Sintaxe | Característica |
|---|---|---|
| Normal | struct { } | Layout definido pelo compilador |
| Extern | extern struct { } | Layout compatível com C ABI |
| Packed | packed struct { } | Sem padding, campos bit a bit |
Armadilhas Comuns
- Esquecer o
self: Métodos de instância recebemselfcomo primeiro parâmetro. Métodos “estáticos” não recebem. - Mutabilidade: Para modificar campos, o parâmetro deve ser
*Self(ponteiro mutável), nãoSelf(cópia). - Ordem de inicialização: Todos os campos sem valor default devem ser inicializados. O compilador emite erro se algum for esquecido.
- Confundir tipos de struct:
struct,extern structepacked structtêm layouts de memória diferentes. Escolha o correto para cada uso.
Boas Práticas com Structs
Use const Self = @This() para evitar repetir o nome da struct em métodos, especialmente em structs genéricas:
const Contador = struct {
valor: u64 = 0,
const Self = @This();
pub fn incrementar(self: *Self) void {
self.valor += 1;
}
pub fn resetar(self: *Self) void {
self.valor = 0;
}
};
Implemente init e deinit como convenção: A biblioteca padrão do Zig usa esse padrão amplamente. init inicializa (podendo retornar erro), e deinit libera recursos. Isso facilita o uso com defer:
var contador = Contador{ .valor = 0 };
// structs simples não precisam de init/deinit
Prefira composição a herança: Zig não tem herança. Para reutilizar comportamento, inclua outras structs como campos ou use comptime para gerar código genérico.
Comparação com Outras Linguagens
Em C, structs são apenas agrupamentos de dados sem métodos. Em C++, structs podem ter métodos e são praticamente idênticas a classes (a diferença é que membros são public por padrão). Em Rust, struct + impl separados. Em Zig, os métodos ficam dentro do bloco da struct, o que é mais próximo de C++, mas sem herança, templates complexos ou polimorfismo implícito. A diferença fundamental é que em Zig o polimorfismo é explícito via comptime ou via tagged unions — sem vtables ocultas ou custo invisível de runtime.
Termos Relacionados
- Packed Struct — Structs com layout exato
- Enum — Enumerações
- Tagged Union — Unions discriminadas
- Comptime — Geração de tipos em compilação