Cheatsheet Zig em Português: Referência Rápida | ZigLang Brasil

Cheatsheet Zig — Referência Completa em Português

Esta é a referência rápida completa da linguagem Zig em uma única página. Ideal para salvar nos favoritos e consultar no dia a dia. Se preferir páginas individuais por tópico, veja a coleção de cheatsheets.


1. Variáveis e Tipos

Para detalhes completos, consulte o cheatsheet de sintaxe básica e o cheatsheet de tipos de dados.

Declarações

const x: i32 = 42;        // imutável — não pode ser reatribuída
var y: i32 = 10;           // mutável — pode ser reatribuída
y = 20;

// Inferência de tipo — o compilador deduz o tipo
const nome = "Zig Brasil"; // tipo: *const [10:0]u8
var flag = true;            // tipo: bool

Tipos Primitivos

// Inteiros com sinal: i8, i16, i32, i64, i128, isize
// Inteiros sem sinal: u8, u16, u32, u64, u128, usize

const a: u8 = 255;        // 0 a 255
const b: i32 = -42;       // -2^31 a 2^31-1
const c: f64 = 3.14159;   // ponto flutuante 64 bits
const d: f32 = 2.71;      // ponto flutuante 32 bits
const e: bool = true;     // true ou false
const f: u8 = 'A';        // caractere é u8 (valor 65)

Optionals

// Optional: o valor pode ser null
var maybe: ?i32 = 42;
maybe = null; // agora é null

// Desempacotar (unwrap) com orelse
const valor = maybe orelse 0; // se null, usa 0

// Unwrap inseguro — causa panic se for null
const forçado = maybe.?;

// Ponteiro optional
var ptr: ?*i32 = null;

Comptime Types

// comptime_int: inteiro de precisão arbitrária em tempo de compilação
const big = 1_000_000_000_000_000;

// comptime_float: flutuante de precisão arbitrária em compilação
const pi = 3.14159265358979323846;

// type: o tipo dos tipos — usado em metaprogramação
fn maxInt(comptime T: type) T {
    return switch (@typeInfo(T)) {
        .int => |info| if (info.signedness == .signed)
            @as(T, (1 << (info.bits - 1)) - 1)
        else
            @as(T, @bitCast(@as(std.meta.Int(.signed, info.bits), -1))),
        else => @compileError("maxInt requer tipo inteiro"),
    };
}

Coerção de Tipos

const a: u8 = 42;
const b: u16 = a;       // coerção implícita: u8 -> u16 (widening)
const c: u8 = @intCast(b); // conversão explícita: u16 -> u8 (narrowing)

const f: f32 = 3.14;
const g: f64 = f;       // coerção implícita: f32 -> f64
const h: f32 = @floatCast(g); // conversão explícita: f64 -> f32

const i: i32 = @intFromFloat(f); // float -> int
const j: f64 = @floatFromInt(a); // int -> float

2. Controle de Fluxo

Veja mais exemplos no cheatsheet de controle de fluxo.

If / Else

const x: i32 = 10;

if (x > 5) {
    // ...
} else if (x > 0) {
    // ...
} else {
    // ...
}

// If como expressão
const resultado = if (x > 0) @as(i32, 1) else @as(i32, -1);

// If com optionals — desempacota o valor
const maybe: ?i32 = 42;
if (maybe) |valor| {
    std.debug.print("Tem valor: {}\n", .{valor});
} else {
    std.debug.print("É null\n", .{});
}

For

// Iteração sobre slices
const itens = [_]i32{ 10, 20, 30, 40 };
for (itens) |item| {
    std.debug.print("{} ", .{item});
}

// Com índice
for (itens, 0..) |item, i| {
    std.debug.print("[{}]={} ", .{i, item});
}

// Iteração sobre range
for (0..5) |i| {
    std.debug.print("{} ", .{i}); // imprime: 0 1 2 3 4
}

// Múltiplos slices simultaneamente
const a = [_]i32{1, 2, 3};
const b = [_]i32{10, 20, 30};
for (a, b) |x, y| {
    std.debug.print("{} ", .{x + y}); // imprime: 11 22 33
}

While

var i: u32 = 0;
while (i < 10) : (i += 1) {
    if (i == 5) continue;
    if (i == 8) break;
    std.debug.print("{} ", .{i});
}

// While com optional
var iter = criarIterador();
while (iter.next()) |valor| {
    std.debug.print("{} ", .{valor});
}

Switch

const cor: enum { vermelho, verde, azul } = .verde;

const nome = switch (cor) {
    .vermelho => "Vermelho",
    .verde => "Verde",
    .azul => "Azul",
};

// Switch com ranges
const nota: u8 = 85;
const conceito = switch (nota) {
    0...59 => "F",
    60...69 => "D",
    70...79 => "C",
    80...89 => "B",
    90...100 => "A",
    else => "Inválido",
};

Labels e Break/Continue

// Labels permitem controlar loops aninhados
outer: for (0..10) |i| {
    inner: for (0..10) |j| {
        if (j == 5) continue :inner;
        if (i * 10 + j > 50) break :outer;
    }
}

// While com label retornando valor
const resultado = blk: {
    var soma: i32 = 0;
    for (0..10) |i| {
        soma += @as(i32, @intCast(i));
    }
    break :blk soma;
};

3. Funções

Referência detalhada no cheatsheet de funções.

const std = @import("std");

// Função básica
fn somar(a: i32, b: i32) i32 {
    return a + b;
}

// Função sem retorno
fn logar(mensagem: []const u8) void {
    std.debug.print("{s}\n", .{mensagem});
}

// Função com erro possível
fn dividir(a: f64, b: f64) !f64 {
    if (b == 0.0) return error.DivisaoPorZero;
    return a / b;
}

// Parâmetros comptime — funções genéricas
fn max(comptime T: type, a: T, b: T) T {
    return if (a > b) a else b;
}

// Chamada:
const m = max(i32, 10, 20); // m = 20

// Funções inline — sempre expandidas no local de chamada
inline fn quadrado(x: i32) i32 {
    return x * x;
}

// Funções extern — interop com C
extern "c" fn printf(format: [*:0]const u8, ...) c_int;

// Ponteiro de função
const Operacao = *const fn (i32, i32) i32;
const op: Operacao = somar;
const resultado = op(3, 4); // resultado = 7

4. Estruturas de Dados

Para mais detalhes: cheatsheet de structs, arrays e slices.

Structs

const Ponto = struct {
    x: f64,
    y: f64,
    z: f64 = 0.0, // valor padrão

    // Método (recebe self)
    pub fn distancia(self: Ponto, outro: Ponto) f64 {
        const dx = self.x - outro.x;
        const dy = self.y - outro.y;
        const dz = self.z - outro.z;
        return @sqrt(dx * dx + dy * dy + dz * dz);
    }

    // Função associada (sem self — como static em outras linguagens)
    pub fn origem() Ponto {
        return .{ .x = 0, .y = 0, .z = 0 };
    }
};

const p = Ponto{ .x = 3.0, .y = 4.0 };
const dist = p.distancia(Ponto.origem());

Arrays e Slices

// Array de tamanho fixo
const arr = [5]u8{ 1, 2, 3, 4, 5 };
const arr2 = [_]u8{ 1, 2, 3 }; // tamanho inferido: 3

// Slice — referência a uma parte de um array
const slice: []const u8 = arr[1..4]; // {2, 3, 4}

// Array com valor repetido
const zeros = [_]u8{0} ** 100; // 100 zeros

// Concatenação em comptime
const hello = "hello" ++ " " ++ "world";

ArrayList e HashMap

const std = @import("std");
const allocator = std.heap.page_allocator;

// ArrayList — lista dinâmica
var lista = std.ArrayList(i32).init(allocator);
defer lista.deinit();

try lista.append(10);
try lista.append(20);
try lista.appendSlice(&[_]i32{ 30, 40 });

for (lista.items) |item| {
    std.debug.print("{} ", .{item});
}

// HashMap — tabela hash
var mapa = std.StringHashMap(i32).init(allocator);
defer mapa.deinit();

try mapa.put("idade", 25);
try mapa.put("nivel", 42);

if (mapa.get("idade")) |valor| {
    std.debug.print("idade = {}\n", .{valor});
}

Enums e Tagged Unions

// Enum
const Cor = enum(u8) {
    vermelho = 1,
    verde = 2,
    azul = 3,
};

// Tagged union — como Rust enums (para comparação com [Rust](https://rustlang.com.br))
const Forma = union(enum) {
    circulo: f64,           // raio
    retangulo: struct { largura: f64, altura: f64 },
    ponto: void,

    pub fn area(self: Forma) f64 {
        return switch (self) {
            .circulo => |r| std.math.pi * r * r,
            .retangulo => |ret| ret.largura * ret.altura,
            .ponto => 0.0,
        };
    }
};

5. Strings e Formatação

Consulte também o cheatsheet de strings e o cheatsheet de formatação.

const std = @import("std");

// String literal — tipo *const [N:0]u8 (terminada com sentinela 0)
const saudacao = "Olá, Zig!";

// String multiline (preserva indentação relativa)
const sql =
    \\SELECT *
    \\FROM usuarios
    \\WHERE ativo = true
    \\ORDER BY nome;
;

// Slice de string
const nome = "Zig Brasil";
const sub = nome[0..3]; // "Zig"

// Formatação com std.fmt
var buf: [256]u8 = undefined;
const msg = try std.fmt.bufPrint(&buf, "Idade: {d}, Nome: {s}", .{ 25, "Ana" });
std.debug.print("{s}\n", .{msg});

// Impressão direta
std.debug.print("Valor: {d:.2}\n", .{3.14159}); // "Valor: 3.14"
std.debug.print("Hex: {x}\n", .{@as(u32, 255)});   // "Hex: ff"
std.debug.print("Binário: {b}\n", .{@as(u8, 42)});  // "Binário: 101010"

// Comparação de strings
const iguais = std.mem.eql(u8, "abc", "abc"); // true

// Encontrar substring
if (std.mem.indexOf(u8, "Olá Mundo", "Mundo")) |pos| {
    std.debug.print("Encontrado na posição {}\n", .{pos});
}

// Split
var iter = std.mem.splitScalar(u8, "a,b,c,d", ',');
while (iter.next()) |parte| {
    std.debug.print("{s} ", .{parte});
}

6. Tratamento de Erros

Para uma referência completa, veja o cheatsheet de error handling.

Diferente de linguagens com exceções ou de Go que retorna múltiplos valores (valor, err), Zig usa error unions — um tipo que combina o resultado e o erro em um único valor no type system.

// Error set — definição de erros possíveis
const ArquivoErro = error{
    NaoEncontrado,
    PermissaoNegada,
    Corrompido,
};

// Função que retorna error union
fn abrirArquivo(nome: []const u8) ArquivoErro!Arquivo {
    if (nome.len == 0) return error.NaoEncontrado;
    // ...
}

// try — propaga o erro automaticamente (como ? em Rust)
fn processar() !void {
    const arq = try abrirArquivo("dados.txt");
    defer arq.close();
    // ...
}

// catch — captura e trata o erro
const resultado = abrirArquivo("x.txt") catch |err| {
    std.debug.print("Erro: {}\n", .{err});
    return;
};

// catch com valor padrão
const valor = parseInt("abc") catch 0;

// errdefer — executa apenas se a função retornar erro
fn criarRecurso(allocator: std.mem.Allocator) !*Recurso {
    const mem = try allocator.create(Recurso);
    errdefer allocator.destroy(mem); // limpa se algo der errado depois
    try mem.inicializar();
    return mem;
}

// Combinando error sets
const ErroIO = error{ Leitura, Escrita };
const ErroParseamento = error{ FormatoInvalido, Overflow };
const ErroTotal = ErroIO || ErroParseamento; // união dos dois conjuntos

7. Comptime e Metaprogramação

Referência detalhada no cheatsheet de comptime.

O comptime é o diferencial de Zig para metaprogramação. Em vez de macros complexas (como em C/C++) ou generics com trait bounds (como em Rust), Zig executa código regular em tempo de compilação.

const std = @import("std");

// Bloco comptime — executado pelo compilador
const tabela = comptime blk: {
    var t: [256]u8 = undefined;
    for (0..256) |i| {
        t[i] = @as(u8, @intCast(i)) *% 2;
    }
    break :blk t;
};

// Função genérica com comptime
fn List(comptime T: type) type {
    return struct {
        items: []T,
        len: usize,

        const Self = @This();

        pub fn get(self: Self, index: usize) T {
            return self.items[index];
        }
    };
}

const IntList = List(i32); // tipo gerado em compilação

// @typeInfo — introspecção de tipos
fn nomeDoTipo(comptime T: type) []const u8 {
    return @typeName(T);
}

// @TypeOf — obtém o tipo de uma expressão
const x: i32 = 10;
const T = @TypeOf(x); // T é o tipo i32

// Comptime com strings
fn repete(comptime s: []const u8, comptime n: usize) *const [s.len * n]u8 {
    return comptime blk: {
        var resultado: [s.len * n]u8 = undefined;
        for (0..n) |i| {
            @memcpy(resultado[i * s.len ..][0..s.len], s);
        }
        break :blk &resultado;
    };
}

// Verificação de tipo em comptime
fn somar(a: anytype, b: anytype) @TypeOf(a) {
    const T = @TypeOf(a);
    if (@TypeOf(b) != T) @compileError("Tipos incompatíveis");
    return a + b;
}

8. Allocators

Veja o cheatsheet completo de allocators para padrões avançados.

Diferente de C com malloc/free global ou de Go com garbage collector, Zig exige que você passe explicitamente um allocator para cada alocação. Isso dá controle total sobre a memória — semelhante à filosofia de Rust de gerenciamento explícito, mas sem o borrow checker.

const std = @import("std");

// 1. page_allocator — aloca páginas diretamente do SO
const page = std.heap.page_allocator;

// 2. GeneralPurposeAllocator — uso geral com detecção de leaks
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer {
    const check = gpa.deinit();
    if (check == .leak) @panic("Vazamento de memória detectado!");
}
const gpa_alloc = gpa.allocator();

// 3. ArenaAllocator — alocações em bloco, free tudo de uma vez
var arena = std.heap.ArenaAllocator.init(page);
defer arena.deinit(); // libera toda a memória de uma vez
const arena_alloc = arena.allocator();

// 4. FixedBufferAllocator — aloca dentro de um buffer fixo (sem heap)
var buffer: [4096]u8 = undefined;
var fba = std.heap.FixedBufferAllocator.init(&buffer);
const fba_alloc = fba.allocator();

// Padrão de uso: alocar e liberar
const dados = try gpa_alloc.alloc(u8, 1024);
defer gpa_alloc.free(dados);

// Criar uma struct na heap
const ponto = try gpa_alloc.create(Ponto);
defer gpa_alloc.destroy(ponto);
ponto.* = .{ .x = 1.0, .y = 2.0 };

// Padrão para funções: receber allocator como parâmetro
fn processarDados(allocator: std.mem.Allocator, entrada: []const u8) ![]u8 {
    var resultado = try allocator.alloc(u8, entrada.len * 2);
    // ... processar ...
    return resultado;
    // quem chamou é responsável por fazer free do resultado
}

9. Interop com C

Consulte o cheatsheet de interop com C para mais detalhes.

Um dos pontos fortes de Zig é a interoperabilidade nativa com C — sem bindings manuais, sem FFI complexo.

const std = @import("std");

// Importar headers C
const c = @cImport({
    @cInclude("stdio.h");
    @cInclude("stdlib.h");
    @cInclude("string.h");
});

// Chamar funções C diretamente
pub fn main() void {
    _ = c.printf("Olá do C! %d\n", @as(c_int, 42));

    // Alocar memória via malloc (não recomendado — prefira allocators Zig)
    const ptr: ?*anyopaque = c.malloc(100);
    if (ptr) |p| {
        defer c.free(p);
        // usar a memória...
    }
}

// Exportar função para C
export fn zig_somar(a: i32, b: i32) i32 {
    return a + b;
}

// Converter entre strings Zig e C
fn zigParaC(s: []const u8) [*:0]const u8 {
    return @ptrCast(s.ptr); // funciona se s já é null-terminated
}

fn cParaZig(s: [*:0]const u8) []const u8 {
    return std.mem.span(s);
}

// No build.zig — linkar biblioteca C
// exe.linkLibC();
// exe.linkSystemLibrary("sqlite3");
// exe.addIncludePath(.{ .cwd_relative = "/usr/include" });

10. Comandos Úteis

Referência completa no cheatsheet de comandos CLI.

# Compilar e executar diretamente
zig run arquivo.zig

# Compilar para binário
zig build-exe arquivo.zig

# Build com build.zig (modo padrão)
zig build

# Build em modo release
zig build -Doptimize=ReleaseFast

# Executar testes
zig test arquivo.zig

# Formatar código
zig fmt arquivo.zig
zig fmt .         # formata todo o diretório

# Zig como compilador C/C++
zig cc -o programa main.c
zig c++ -o programa main.cpp

# Cross-compilation (um dos super poderes do Zig!)
zig build-exe -target x86_64-linux-gnu arquivo.zig
zig build-exe -target aarch64-macos arquivo.zig
zig build-exe -target x86_64-windows-gnu arquivo.zig

# Traduzir C para Zig
zig translate-c header.h > traduzido.zig

# Ver documentação da stdlib
zig std

build.zig Básico

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{
        .name = "meu-projeto",
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);

    // Comando "zig build run"
    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Executar o programa");
    run_step.dependOn(&run_cmd.step);

    // Comando "zig build test"
    const testes = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });
    const run_testes = b.addRunArtifact(testes);
    const test_step = b.step("test", "Rodar testes unitários");
    test_step.dependOn(&run_testes.step);
}

11. Dicas e Truques

defer e errdefer

// defer — executa quando sai do escopo (LIFO)
fn processar() !void {
    const arq = try std.fs.cwd().openFile("dados.txt", .{});
    defer arq.close(); // sempre fecha, mesmo com erro

    const lock = try adquirirLock();
    defer lock.release(); // liberado depois de arq.close()

    // ... lógica principal ...
}

Packed Structs

// Layout exato de memória — útil para protocolos binários e hardware
const PacketHeader = packed struct {
    version: u4,     // 4 bits
    tipo: u4,        // 4 bits
    tamanho: u16,    // 16 bits
    flags: u8,       // 8 bits
}; // total: exatamente 4 bytes

Sentinel-Terminated Arrays

// Array terminado com sentinela (como strings C)
const dados: [5:0]u8 = .{ 72, 101, 108, 108, 111 }; // "Hello" + \0
const str: [:0]const u8 = "Terminado em zero";

// Conversão
const c_str: [*:0]const u8 = str.ptr;
const zig_str: []const u8 = std.mem.span(c_str);

Padrões Comuns

// Struct com método init (padrão construtor)
const Servidor = struct {
    porta: u16,
    conexoes: u32 = 0,

    pub fn init(porta: u16) Servidor {
        return .{ .porta = porta };
    }
};

// Usando @as para dicas de tipo
const x = @as(u32, 42);

// Struct anônima (tuple)
const tupla = .{ "nome", 42, true };
std.debug.print("{s} tem {} anos\n", .{ tupla[0], tupla[1] });

// Iteração sobre campos de struct em comptime
fn debugStruct(valor: anytype) void {
    const info = @typeInfo(@TypeOf(valor)).@"struct";
    inline for (info.fields) |campo| {
        std.debug.print("{s}: {any}\n", .{ campo.name, @field(valor, campo.name) });
    }
}

// Bloco de teste (integrado na linguagem)
test "somar funciona" {
    const resultado = somar(2, 3);
    try std.testing.expectEqual(@as(i32, 5), resultado);
}

test "divisão por zero retorna erro" {
    const resultado = dividir(10.0, 0.0);
    try std.testing.expectError(error.DivisaoPorZero, resultado);
}

Comparação Rápida: Zig vs Rust vs Go

AspectoZigRustGo
Gerenciamento de memóriaManual com allocatorsOwnership + borrow checkerGarbage collector
Segurança em runtimeOpcional (ReleaseFast)Garantida em compilaçãoGarbage collector + bounds checking
Interop com CNativo (sem overhead)Via FFI (unsafe)Via cgo (overhead)
MetaprogramaçãocomptimeMacros + genericsgo generate (limitado)
Null safetyOptionals (?T)Option<T>Ponteiros nil
Tratamento de errosError unions (!T)Result<T, E>Múltiplos retornos
Cross-compilationEmbutido no compiladorVia targets (rustup)Embutido (GOOS/GOARCH)

Gostou desta referência? Explore os cheatsheets individuais para informações ainda mais detalhadas sobre cada tópico, incluindo enums e unions, ponteiros, testes, concorrência e operações de I/O.

Continue aprendendo Zig

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