Criar seu Primeiro Projeto Zig — Do Zero ao Executável

Criar seu Primeiro Projeto Zig

Parabéns por instalar o Zig! Zig é uma linguagem de programação de sistemas que combina performance de C com ferramentas modernas de desenvolvimento. Agora é hora de criar seu primeiro projeto. Este guia vai levá-lo do zero até um projeto funcional com compilação, execução, testes e uma estrutura profissional.

Certifique-se de que o Zig está instalado corretamente executando zig version. Se ainda não instalou, consulte o guia completo de instalação para sua plataforma.


Hello World: O Jeito Rápido

Antes de criar um projeto completo, vamos começar com o jeito mais rápido de executar código Zig.

Crie um arquivo hello.zig:

const std = @import("std");

pub fn main() void {
    std.debug.print("Olá, mundo! Bem-vindo ao Zig!\n", .{});
}

Execute diretamente com:

zig run hello.zig

Saída:

Olá, mundo! Bem-vindo ao Zig!

O zig run compila e executa o programa em um único passo. Isso é ótimo para testes rápidos, mas para projetos reais, vamos usar o sistema de build do Zig.


Criando um Projeto com zig init

O Zig tem um comando integrado para criar a estrutura inicial de um projeto:

mkdir meu-projeto && cd meu-projeto
zig init

Isso cria a seguinte estrutura:

meu-projeto/
├── build.zig
├── build.zig.zon
└── src/
    ├── main.zig
    └── root.zig

Entendendo os Arquivos

build.zig — Sistema de Build

O build.zig é o coração do sistema de build do Zig. Diferente de Makefiles ou CMakeLists, ele é escrito em Zig. Para um aprofundamento completo sobre todas as capacidades do sistema de build, incluindo dependências externas e steps customizados, consulte o guia completo do Zig Build System.

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);

    const run_cmd = b.addRunArtifact(exe);
    run_cmd.step.dependOn(b.getInstallStep());

    if (b.args) |args| {
        run_cmd.addArgs(args);
    }

    const run_step = b.step("run", "Run the application");
    run_step.dependOn(&run_cmd.step);

    const unit_tests = b.addTest(.{
        .root_source_file = b.path("src/main.zig"),
        .target = target,
        .optimize = optimize,
    });

    const run_unit_tests = b.addRunArtifact(unit_tests);
    const test_step = b.step("test", "Run unit tests");
    test_step.dependOn(&run_unit_tests.step);
}

build.zig.zon — Dependências

O arquivo .zon (Zig Object Notation) define metadados e dependências do projeto:

.{
    .name = "meu-projeto",
    .version = "0.0.0",
    .dependencies = .{},
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

src/main.zig — Ponto de Entrada

const std = @import("std");

pub fn main() !void {
    std.debug.print("All your {s} are belong to us.\n", .{"codebase"});
}

Compilar e Executar

Compilar o Projeto

zig build

O executável é criado em zig-out/bin/meu-projeto.

Executar o Projeto

zig build run

Ou execute o binário diretamente:

./zig-out/bin/meu-projeto

Compilar com Otimizações

# Debug (padrão) — com símbolos de debug, sem otimização
zig build

# ReleaseSafe — otimizado, com verificações de segurança
zig build -Doptimize=ReleaseSafe

# ReleaseFast — máxima performance, sem verificações
zig build -Doptimize=ReleaseFast

# ReleaseSmall — menor tamanho do binário
zig build -Doptimize=ReleaseSmall

Escrevendo Código Real

Vamos substituir o src/main.zig gerado por algo mais interessante. Aqui está um programa que demonstra vários conceitos do Zig:

const std = @import("std");

const Tarefa = struct {
    descricao: []const u8,
    concluida: bool,

    pub fn nova(descricao: []const u8) Tarefa {
        return .{
            .descricao = descricao,
            .concluida = false,
        };
    }

    pub fn concluir(self: *Tarefa) void {
        self.concluida = true;
    }

    pub fn exibir(self: Tarefa, writer: anytype) !void {
        const status: []const u8 = if (self.concluida) "[x]" else "[ ]";
        try writer.print("{s} {s}\n", .{ status, self.descricao });
    }
};

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    try stdout.print("=== Lista de Tarefas em Zig ===\n\n", .{});

    var tarefas = [_]Tarefa{
        Tarefa.nova("Instalar o Zig"),
        Tarefa.nova("Configurar o editor"),
        Tarefa.nova("Criar o primeiro projeto"),
        Tarefa.nova("Aprender structs e enums"),
        Tarefa.nova("Escrever testes"),
    };

    // Marcar as primeiras tarefas como concluídas
    tarefas[0].concluir();
    tarefas[1].concluir();
    tarefas[2].concluir();

    // Exibir todas as tarefas
    for (&tarefas) |*tarefa| {
        try tarefa.exibir(stdout);
    }

    // Contar tarefas concluídas
    var concluidas: u32 = 0;
    for (tarefas) |t| {
        if (t.concluida) concluidas += 1;
    }

    try stdout.print("\nProgresso: {}/{} tarefas concluidas\n", .{ concluidas, tarefas.len });
}

Execute com:

zig build run

Saída esperada:

=== Lista de Tarefas em Zig ===

[x] Instalar o Zig
[x] Configurar o editor
[x] Criar o primeiro projeto
[ ] Aprender structs e enums
[ ] Escrever testes

Progresso: 3/5 tarefas concluidas

Escrevendo Testes

O Zig tem um sistema de testes integrado. Adicione testes diretamente no mesmo arquivo ou em arquivos separados.

Testes no Próprio Arquivo

Crie src/calculadora.zig:

const std = @import("std");

pub fn somar(a: i32, b: i32) i32 {
    return a + b;
}

pub fn subtrair(a: i32, b: i32) i32 {
    return a - b;
}

pub fn multiplicar(a: i32, b: i32) i32 {
    return a * b;
}

pub fn dividir(a: i32, b: i32) !f64 {
    if (b == 0) return error.DivisaoPorZero;
    return @as(f64, @floatFromInt(a)) / @as(f64, @floatFromInt(b));
}

// Testes
test "somar numeros positivos" {
    try std.testing.expectEqual(@as(i32, 5), somar(2, 3));
    try std.testing.expectEqual(@as(i32, 0), somar(-1, 1));
}

test "subtrair numeros" {
    try std.testing.expectEqual(@as(i32, 1), subtrair(3, 2));
    try std.testing.expectEqual(@as(i32, -5), subtrair(0, 5));
}

test "multiplicar numeros" {
    try std.testing.expectEqual(@as(i32, 6), multiplicar(2, 3));
    try std.testing.expectEqual(@as(i32, 0), multiplicar(0, 100));
}

test "dividir numeros" {
    const resultado = try dividir(10, 2);
    try std.testing.expectEqual(@as(f64, 5.0), resultado);
}

test "dividir por zero retorna erro" {
    const resultado = dividir(10, 0);
    try std.testing.expectError(error.DivisaoPorZero, resultado);
}

Executar os Testes

# Testar o arquivo diretamente
zig test src/calculadora.zig

# Testar via build system
zig build test

Saída esperada:

All 5 passed.

Importar no main.zig

Atualize o src/main.zig para usar a calculadora:

const std = @import("std");
const calc = @import("calculadora.zig");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const a: i32 = 42;
    const b: i32 = 8;

    try stdout.print("Calculadora Zig\n", .{});
    try stdout.print("{} + {} = {}\n", .{ a, b, calc.somar(a, b) });
    try stdout.print("{} - {} = {}\n", .{ a, b, calc.subtrair(a, b) });
    try stdout.print("{} * {} = {}\n", .{ a, b, calc.multiplicar(a, b) });

    if (calc.dividir(a, b)) |resultado| {
        try stdout.print("{} / {} = {d:.2}\n", .{ a, b, resultado });
    } else |err| {
        try stdout.print("Erro na divisão: {}\n", .{err});
    }
}

Para que o build.zig reconheça o novo módulo, o src/calculadora.zig é encontrado automaticamente quando importado pelo main.zig usando @import("calculadora.zig").


Estrutura de Projeto Profissional

Para projetos maiores, organize assim:

meu-projeto/
├── build.zig
├── build.zig.zon
├── src/
│   ├── main.zig           # Ponto de entrada
│   ├── lib.zig             # API pública da biblioteca
│   ├── calculadora.zig     # Módulo da calculadora
│   └── utils/
│       └── helpers.zig     # Utilitários
├── tests/
│   └── integration.zig    # Testes de integração
└── .gitignore

.gitignore para Projetos Zig

# Zig build artifacts
zig-out/
zig-cache/
.zig-cache/

# Editor
.vscode/
.idea/

# OS
.DS_Store
Thumbs.db

Lendo Entrada do Usuário

Aqui está um exemplo de programa interativo que lê entrada do terminal:

const std = @import("std");

pub fn main() !void {
    const stdin = std.io.getStdIn().reader();
    const stdout = std.io.getStdOut().writer();

    try stdout.print("Qual é o seu nome? ", .{});

    var buf: [256]u8 = undefined;
    if (try stdin.readUntilDelimiterOrEof(&buf, '\n')) |line| {
        const nome = std.mem.trimRight(u8, line, "\r\n");
        try stdout.print("Olá, {s}! Bem-vindo ao Zig!\n", .{nome});
    }
}

Próximos Passos

Com seu primeiro projeto criado:

  1. Aprenda mais sobre a linguagemIntrodução ao Zig
  2. Configure seu editorVS Code, Neovim ou outros
  3. Gerencie versõesGerenciar versões do Zig
  4. Configure CI/CDGitHub Actions com Zig
  5. Veja exemplos avançadosReceitas
  6. Resolva problemasErros comuns

Continue praticando e explorando a linguagem. O Zig tem uma curva de aprendizado, mas as recompensas valem o esforço!

Continue aprendendo Zig

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