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
| Aspecto | Zig | Rust | Go |
|---|---|---|---|
| Gerenciamento de memória | Manual com allocators | Ownership + borrow checker | Garbage collector |
| Segurança em runtime | Opcional (ReleaseFast) | Garantida em compilação | Garbage collector + bounds checking |
| Interop com C | Nativo (sem overhead) | Via FFI (unsafe) | Via cgo (overhead) |
| Metaprogramação | comptime | Macros + generics | go generate (limitado) |
| Null safety | Optionals (?T) | Option<T> | Ponteiros nil |
| Tratamento de erros | Error unions (!T) | Result<T, E> | Múltiplos retornos |
| Cross-compilation | Embutido no compilador | Via 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.