Introdução
Se você vem do Go, Zig vai parecer familiar em alguns aspectos e surpreendente em outros. Ambas as linguagens valorizam simplicidade, compilação rápida e binários estáticos. Porém, Zig opera em um nível mais baixo — sem garbage collector, sem runtime, e com controle explícito de memória.
Este guia mapeia conceitos de Go para Zig, ajudando você a aproveitar seu conhecimento existente. Para um guia de migração completo, veja Guia de Migração: Go para Zig. Para a comparação detalhada, consulte Zig vs Go.
Mapeamento Rápido de Conceitos
| Go | Zig | Notas |
|---|---|---|
var x int = 42 | var x: i32 = 42 | Tipos explícitos |
x := 42 | const x = 42 | Inferência de tipo |
func | fn | Funções |
struct | struct | Similar |
interface | Comptime + tipo genérico | Sem interfaces de runtime |
goroutine | std.Thread / async | Sem green threads |
channel | Sem equivalente direto | Usar mutex/atomic |
defer | defer | Muito similar |
error | Error unions | Mais tipado |
nil | null | Para optionals |
slice | Slice []T | Conceito similar |
| GC | Allocators | Diferença fundamental |
Variáveis e Tipos
Go
var nome string = "Zig Brasil"
idade := 25
pi := 3.14
ativo := true
Zig
const nome: []const u8 = "Zig Brasil";
var idade: u32 = 25;
const pi: f64 = 3.14;
const ativo: bool = true;
Em Zig, const é o padrão — variáveis são imutáveis por default. Use var apenas quando precisar mutar. Em Go, todo valor é mutável.
Structs e Métodos
Go
type Ponto struct {
X, Y float64
}
func (p Ponto) Distancia(outro Ponto) float64 {
dx := p.X - outro.X
dy := p.Y - outro.Y
return math.Sqrt(dx*dx + dy*dy)
}
Zig
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 std.math.sqrt(dx * dx + dy * dy);
}
};
Em Zig, métodos são funções dentro do namespace da struct. O self é explícito, não implícito como o receiver em Go.
Interfaces vs Comptime
Go usa interfaces de runtime com dispatch dinâmico. Zig não tem interfaces — em vez disso, usa comptime para polimorfismo:
Go
type Writer interface {
Write(p []byte) (n int, err error)
}
func escrever(w Writer, dados []byte) error {
_, err := w.Write(dados)
return err
}
Zig
fn escrever(writer: anytype, dados: []const u8) !void {
try writer.writeAll(dados);
}
// OU com tipo explícito via comptime
fn escreverGenerico(comptime Writer: type, writer: Writer, dados: []const u8) !void {
try writer.writeAll(dados);
}
O compilador Zig verifica em tempo de compilação se o tipo passado tem o método necessário. Não há vtable ou dispatch dinâmico.
Error Handling
Go
func dividir(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("divisão por zero")
}
return a / b, nil
}
resultado, err := dividir(10, 3)
if err != nil {
log.Fatal(err)
}
Zig
const DivisaoError = error{DivisaoPorZero};
fn dividir(a: f64, b: f64) DivisaoError!f64 {
if (b == 0) return error.DivisaoPorZero;
return a / b;
}
const resultado = dividir(10, 3) catch |err| {
std.debug.print("Erro: {}\n", .{err});
return;
};
// Ou com try (propaga o erro para cima)
const resultado2 = try dividir(10, 3);
Zig combina o valor e o erro em um único tipo (error union), eliminando a necessidade de verificar if err != nil manualmente. Veja Error Sets Customizados e Padrões Try/Catch.
Slices
Slices em Go e Zig são conceitualmente similares, mas com diferenças importantes:
Go
numeros := []int{1, 2, 3, 4, 5}
sub := numeros[1:3] // [2, 3]
numeros = append(numeros, 6)
Zig
const numeros = [_]u32{ 1, 2, 3, 4, 5 };
const sub = numeros[1..3]; // [2, 3]
// Para append, usar ArrayList (requer allocator)
var lista = std.ArrayList(u32).init(allocator);
defer lista.deinit();
try lista.appendSlice(&numeros);
try lista.append(6);
Em Zig, slices não crescem automaticamente. Para coleções dinâmicas, use ArrayList com um allocator explícito.
Goroutines vs Threads
Go
func main() {
ch := make(chan int, 10)
go func() {
ch <- 42
}()
valor := <-ch
fmt.Println(valor)
}
Zig
const std = @import("std");
fn trabalho() void {
// fazer algo
}
pub fn main() !void {
const thread = try std.Thread.spawn(.{}, trabalho, .{});
thread.join();
}
Zig usa threads do sistema operacional, não green threads. Não há channels embutidos — use std.Thread.Mutex, std.atomic, ou implemente suas próprias estruturas de comunicação. Veja Concorrência em Zig.
Gerenciamento de Memória: A Maior Diferença
Em Go, o garbage collector cuida de tudo. Em Zig, o programador gerencia memória explicitamente:
Go
func criarLista() []int {
lista := make([]int, 0, 100)
for i := 0; i < 100; i++ {
lista = append(lista, i)
}
return lista // GC cuida da memória
}
Zig
fn criarLista(allocator: std.mem.Allocator) ![]u32 {
var lista = std.ArrayList(u32).init(allocator);
errdefer lista.deinit();
for (0..100) |i| {
try lista.append(@intCast(i));
}
return lista.toOwnedSlice();
}
// O chamador é responsável por liberar:
const lista = try criarLista(allocator);
defer allocator.free(lista);
Veja Substituir malloc/free por Allocators e ArenaAllocator.
Defer
Ambas as linguagens têm defer, mas com semântica diferente:
- Go:
deferexecuta na saída da função - Zig:
deferexecuta na saída do escopo (bloco)
fn exemplo() void {
{
const recurso = obterRecurso();
defer liberarRecurso(recurso);
// recurso é liberado ao sair deste bloco
}
// recurso já foi liberado aqui
}
Zig também tem errdefer, que executa apenas quando a função retorna um erro — sem equivalente em Go.
Testes
Go
func TestSoma(t *testing.T) {
resultado := Soma(2, 3)
if resultado != 5 {
t.Errorf("esperava 5, obteve %d", resultado)
}
}
Zig
test "soma" {
const resultado = soma(2, 3);
try std.testing.expectEqual(@as(u32, 5), resultado);
}
Testes em Zig são integrados ao arquivo fonte com o bloco test. Execute com zig test arquivo.zig. Veja Testes Unitários Básicos e Testes com Allocator.
Conclusão
Vindo de Go, as maiores adaptações serão o gerenciamento manual de memória e a ausência de garbage collector. Em compensação, Zig oferece performance previsível, binários menores, e controle total sobre o hardware.
A filosofia de simplicidade de Go ressoa em Zig — ambas rejeitam complexidade desnecessária. Se você gosta de Go pela simplicidade, provavelmente vai gostar de Zig pelo mesmo motivo, com a adição de poder trabalhar em nível mais baixo.
Para dar os primeiros passos, visite Introdução ao Zig e Como Instalar Zig.