Struct em Zig — O que é e Como Usar

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

  1. Organização de dados: Agrupam dados relacionados com nomes significativos.
  2. Métodos: Podem conter funções associadas, encapsulando comportamento.
  3. Valores default: Campos podem ter valores padrão, simplificando a criação.
  4. 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

TipoSintaxeCaracterística
Normalstruct { }Layout definido pelo compilador
Externextern struct { }Layout compatível com C ABI
Packedpacked struct { }Sem padding, campos bit a bit

Armadilhas Comuns

  • Esquecer o self: Métodos de instância recebem self como primeiro parâmetro. Métodos “estáticos” não recebem.
  • Mutabilidade: Para modificar campos, o parâmetro deve ser *Self (ponteiro mutável), não Self (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 struct e packed struct tê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

Tutoriais Relacionados

Continue aprendendo Zig

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