Este é o terceiro e último artigo da série “Zig para Iniciantes”. Se você está começando agora, leia primeiro:
Neste guia, vamos explorar todas as estruturas de controle de fluxo em Zig — como seu programa toma decisões e repete ações. Ao final, você terá domínio completo de condicionais, loops e controle de execução.
🎯 Objetivo: Saber usar if, switch, while, for e seus modificadores como um profissional.
Estruturas Condicionais
if e else
A estrutura mais básica de decisão:
const std = @import("std");
pub fn main() void {
const nota: u8 = 85;
if (nota >= 90) {
std.debug.print("Aprovado com A!\n", .{});
} else if (nota >= 80) {
std.debug.print("Aprovado com B!\n", .{});
} else if (nota >= 70) {
std.debug.print("Aprovado com C.\n", .{});
} else {
std.debug.print("Reprovado.\n", .{});
}
}
if como Expressão
Em Zig, if pode retornar valores (similar a operador ternário):
const std = @import("std");
pub fn main() void {
const idade: u8 = 17;
// if como expressão
const status = if (idade >= 18) "maior de idade" else "menor de idade";
std.debug.print("Status: {s}\n", .{status});
// Também funciona com múltiplos ramos
const categoria = if (idade < 13) "criança"
else if (idade < 20) "adolescente"
else if (idade < 60) "adulto"
else "idoso";
std.debug.print("Categoria: {s}\n", .{categoria});
}
if com Optional (Null Safety)
const std = @import("std");
pub fn main() void {
const maybe_number: ?i32 = 42; // Optional (pode ser null)
if (maybe_number) |number| {
std.debug.print("O número é: {d}\n", .{number});
} else {
std.debug.print("Não há número\n", .{});
}
}
if com Error Union
const std = @import("std");
const Erro = error{ DivisaoPorZero };
fn divide(a: i32, b: i32) Erro!i32 {
if (b == 0) return Erro.DivisaoPorZero;
return @divTrunc(a, b);
}
pub fn main() void {
const resultado = divide(10, 2);
if (resultado) |valor| {
std.debug.print("Resultado: {d}\n", .{valor});
} else |err| {
std.debug.print("Erro: {}\n", .{err});
}
}
switch — Múltiplas Condições
Sintaxe Básica
const std = @import("std");
pub fn main() void {
const dia_da_semana: u8 = 3;
const nome_dia = switch (dia_da_semana) {
1 => "Domingo",
2 => "Segunda-feira",
3 => "Terça-feira",
4 => "Quarta-feira",
5 => "Quinta-feira",
6 => "Sexta-feira",
7 => "Sábado",
else => "Dia inválido",
};
std.debug.print("Hoje é {s}\n", .{nome_dia});
}
switch como Expressão
const std = @import("std");
fn conceitoDaNota(nota: u8) []const u8 {
return switch (nota) {
90...100 => "A - Excelente",
80...89 => "B - Bom",
70...79 => "C - Satisfatório",
60...69 => "D - Insuficiente",
0...59 => "F - Reprovado",
else => "Nota inválida",
};
}
pub fn main() void {
const nota: u8 = 85;
std.debug.print("Conceito: {s}\n", .{conceitoDaNota(nota)});
}
Ranges com switch
const std = @import("std");
fn faixaEtaria(idade: u8) []const u8 {
return switch (idade) {
0...2 => "Bebê",
3...12 => "Criança",
13...17 => "Adolescente",
18...59 => "Adulto",
60...120 => "Idoso",
else => "Idade inválida",
};
}
pub fn main() void {
std.debug.print("25 anos: {s}\n", .{faixaEtaria(25)});
std.debug.print("8 anos: {s}\n", .{faixaEtaria(8)});
}
switch com Múltiplos Casos
const std = @import("std");
fn tipoDeDia(dia: u8) []const u8 {
return switch (dia) {
1, 7 => "Fim de semana",
2...6 => "Dia útil",
else => "Inválido",
};
}
pub fn main() void {
std.debug.print("Dia 1: {s}\n", .{tipoDeDia(1)}); // Fim de semana
std.debug.print("Dia 3: {s}\n", .{tipoDeDia(3)}); // Dia útil
}
switch Exhaustivo (Sem else)
Para enums, você pode omitir else se cobrir todos os casos:
const std = @import("std");
const Status = enum {
ativo,
inativo,
pendente,
};
fn descricaoStatus(status: Status) []const u8 {
return switch (status) {
.ativo => "Usuário ativo",
.inativo => "Usuário desativado",
.pendente => "Aguardando ativação",
};
// Não precisa de 'else' porque todos os casos estão cobertos!
}
pub fn main() void {
const status = Status.ativo;
std.debug.print("Status: {s}\n", .{descricaoStatus(status)});
}
Capturando Valores no switch
const std = @import("std");
const Erro = error{ NotFound, PermissionDenied, OutOfMemory };
fn tratarErro(err: Erro) []const u8 {
return switch (err) {
error.NotFound => "Recurso não encontrado",
error.PermissionDenied => "Acesso negado",
error.OutOfMemory => "Memória insuficiente",
};
}
pub fn main() void {
const resultado: Erro!i32 = error.NotFound;
switch (resultado) {
error.NotFound => std.debug.print("Não encontrado!\n", .{}),
error.PermissionDenied => std.debug.print("Sem permissão!\n", .{}),
else => |e| std.debug.print("Erro: {}\n", .{e}),
}
}
Loops — Repetição
while — Loop Condicional
const std = @import("std");
pub fn main() void {
var i: u8 = 1;
while (i <= 5) {
std.debug.print("Contagem: {d}\n", .{i});
i += 1;
}
}
while com Continue Expression
const std = @import("std");
pub fn main() void {
var i: u8 = 1;
// Incremento na própria declaração do while
while (i <= 5) : (i += 1) {
std.debug.print("Contagem: {d}\n", .{i});
}
}
while com Índice
const std = @import("std");
pub fn main() void {
const numeros = [_]i32{ 10, 20, 30, 40, 50 };
var i: usize = 0;
while (i < numeros.len) : (i += 1) {
std.debug.print("numeros[{d}] = {d}\n", .{ i, numeros[i] });
}
}
for — Loop de Iteração
const std = @import("std");
pub fn main() void {
const frutas = [_][]const u8{ "Maçã", "Banana", "Laranja" };
// Itera sobre cada elemento
for (frutas) |fruta| {
std.debug.print("Fruta: {s}\n", .{fruta});
}
}
for com Índice
const std = @import("std");
pub fn main() void {
const notas = [_]f64{ 8.5, 9.0, 7.5, 10.0 };
for (notas, 0..) |nota, indice| {
std.debug.print("Aluno {d}: nota {d:.1}\n", .{ indice + 1, nota });
}
}
for com Range
const std = @import("std");
pub fn main() void {
// Range de 0 a 9 (10 não incluído)
for (0..10) |i| {
std.debug.print("{d} ", .{i});
}
std.debug.print("\n", .{});
// Range de 5 a 15
for (5..15) |i| {
std.debug.print("{d} ", .{i});
}
std.debug.print("\n", .{});
}
for com Múltiplas Sequências
const std = @import("std");
pub fn main() void {
const nomes = [_][]const u8{ "Ana", "Bruno", "Carla" };
const idades = [_]u8{ 25, 30, 22 };
for (nomes, idades) |nome, idade| {
std.debug.print("{s} tem {d} anos\n", .{ nome, idade });
}
}
break e continue — Controle de Loops
break — Interrompe o Loop
const std = @import("std");
pub fn main() void {
var i: u8 = 1;
while (true) { // Loop infinito
if (i > 10) {
break; // Sai do loop
}
std.debug.print("{d} ", .{i});
i += 1;
}
std.debug.print("\nFim!\n", .{});
}
break com Valor
const std = @import("std");
fn encontrarNumero(alvo: i32) ?usize {
const numeros = [_]i32{ 10, 20, 30, 40, 50 };
for (numeros, 0..) |num, index| {
if (num == alvo) {
break index; // Retorna o índice
}
}
return null; // Não encontrado
}
pub fn main() void {
if (encontrarNumero(30)) |index| {
std.debug.print("Encontrado no índice: {d}\n", .{index});
} else {
std.debug.print("Não encontrado\n", .{});
}
}
continue — Pula para Próxima Iteração
const std = @import("std");
pub fn main() void {
// Imprime apenas números ímpares
var i: u8 = 1;
while (i <= 10) : (i += 1) {
if (i % 2 == 0) {
continue; // Pula números pares
}
std.debug.print("{d} ", .{i});
}
std.debug.print("\n", .{});
// Saída: 1 3 5 7 9
}
Loops Aninhados e Labels
Loops Aninhados
const std = @import("std");
pub fn main() void {
var i: u8 = 1;
while (i <= 3) : (i += 1) {
var j: u8 = 1;
while (j <= 3) : (j += 1) {
std.debug.print("({d}, {d}) ", .{ i, j });
}
std.debug.print("\n", .{});
}
}
Labels para break e continue
const std = @import("std");
pub fn main() void {
var i: u8 = 1;
// Label no loop externo
externo: while (i <= 3) : (i += 1) {
var j: u8 = 1;
while (j <= 3) : (j += 1) {
if (i == 2 and j == 2) {
break :externo; // Sai do loop externo
}
std.debug.print("({d}, {d}) ", .{ i, j });
}
std.debug.print("\n", .{});
}
std.debug.print("\nInterrompido no (2,2)\n", .{});
}
inline while e inline for
Loops que se desenrolam em compile-time:
const std = @import("std");
pub fn main() void {
// inline for: o loop "desaparece" em compile-time
inline for (0..5) |i| {
// 'i' é um comptime_int aqui
std.debug.print("Iteração {d}\n", .{i});
}
// Útil para gerar código repetitivo
const Tipos = .{ i32, f64, u8 };
inline for (Tipos) |T| {
std.debug.print("Tipo: {s}\n", .{@typeName(T)});
}
}
Padrões Avançados
Loop com Error Union
const std = @import("std");
const Erro = error{ ValorInvalido };
fn processarValores() !void {
const valores = [_]i32{ 1, 2, -1, 4 };
for (valores) |valor| {
if (valor < 0) {
return Erro.ValorInvalido;
}
std.debug.print("Processando: {d}\n", .{valor});
}
}
pub fn main() void {
processarValores() catch |err| {
std.debug.print("Erro durante processamento: {}\n", .{err});
};
}
Loop com Optional
const std = @import("std");
pub fn main() void {
const valores = [_]?i32{ 10, null, 30, null, 50 };
for (valores) |maybe_valor| {
if (maybe_valor) |valor| {
std.debug.print("Valor: {d}\n", .{valor});
} else {
std.debug.print("Valor ausente\n", .{});
}
}
}
Exercícios Práticos
Exercício 1: Fatorial com while
Calcule o fatorial de um número usando while:
Ver Solução
const std = @import("std");
fn fatorial(n: u32) u64 {
if (n == 0) return 1;
var resultado: u64 = 1;
var i: u32 = 1;
while (i <= n) : (i += 1) {
resultado *= i;
}
return resultado;
}
pub fn main() void {
const n: u32 = 5;
std.debug.print("{d}! = {d}\n", .{ n, fatorial(n) });
}
Exercício 2: Verificador de Número Primo
Verifique se um número é primo:
Ver Solução
const std = @import("std");
fn ehPrimo(n: u32) bool {
if (n <= 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
var i: u32 = 3;
while (i * i <= n) : (i += 2) {
if (n % i == 0) return false;
}
return true;
}
pub fn main() void {
for (1..21) |n| {
const status = if (ehPrimo(@intCast(n))) "primo" else "não primo";
std.debug.print("{d} é {s}\n", .{ n, status });
}
}
Exercício 3: Jogo de Adivinhação Simples
Simule um jogo onde o computador “adivinha” um número (força bruta):
Ver Solução
const std = @import("std");
pub fn main() void {
const numero_secreto: u8 = 42;
var palpite: u8 = 1;
const tentativas = while (palpite <= 100) : (palpite += 1) {
if (palpite == numero_secreto) {
break palpite;
}
} else 0; // Se não encontrar, retorna 0
if (tentativas > 0) {
std.debug.print("Número {d} encontrado em {d} tentativas!\n", .{
numero_secreto, tentativas
});
}
}
Exercício 4: Pirâmide de Números
Crie uma pirâmide numérica:
Ver Solução
const std = @import("std");
pub fn main() void {
const altura: u8 = 5;
var linha: u8 = 1;
while (linha <= altura) : (linha += 1) {
// Espaços
var espaco: u8 = 1;
while (espaco <= altura - linha) : (espaco += 1) {
std.debug.print(" ", .{});
}
// Números crescentes
var num: u8 = 1;
while (num <= linha) : (num += 1) {
std.debug.print("{d} ", .{num});
}
std.debug.print("\n", .{});
}
}
Comparação: Qual Loop Usar?
| Situação | Loop Recomendado | Exemplo |
|---|---|---|
| Iterar array/slice | for | for (items) |item| { ... } |
| Contagem conhecida | for (0..n) | for (0..10) |i| { ... } |
| Condição desconhecida | while | while (condicao) { ... } |
| Loop infinito | while (true) | while (true) { ... } |
| Múltiplas sequências | for (a, b) | for (nomes, idades) |
| Precisa de índice | for (.., 0..) | for (items, 0..) |
| Compile-time | inline for | inline for (0..5) |
Erros Comuns
❌ Esquecer de Incrementar em while
var i = 0;
while (i < 10) {
std.debug.print("{d}\n", .{i});
// ❌ ESQUECEU: i += 1;
}
// Loop infinito!
❌ Modificar Array Durante Iteração
var nums = [_]i32{ 1, 2, 3 };
for (&nums) |*num| {
num.* *= 2; // ✅ OK: modificando elemento
}
❌ Off-by-one Error
const array = [_]i32{ 10, 20, 30 };
// ❌ ERRADO: acessa índice 3 (não existe)
for (0..array.len + 1) |i| { ... }
// ✅ CORRETO
for (0..array.len) |i| { ... }
// ou
for (array, 0..) |val, i| { ... }
Resumo da Série “Zig para Iniciantes”
Parabéns! Você completou a série completa para iniciantes em Zig!
✅ O Que Você Aprendeu
Artigo 1 — Primeiros Passos:
- O que é Zig e por que aprender
- Instalação e configuração
- Primeiro programa
- const vs var
- Tipos básicos
- if, switch, while, for básico
Artigo 2 — Sintaxe Completa:
- Sistema de tipos completo
- Arrays, slices, strings
- Funções avançadas
- Escopo e visibilidade
- Conversão de tipos
Artigo 3 — Controle de Fluxo:
- if/else como expressões
- switch completo (ranges, múltiplos casos)
- while e for avançados
- break e continue
- Loops aninhados com labels
- inline loops
🚀 Próximos Passos
Agora que você tem a base sólida, explore:
- Tratamento de Erros em Zig — Error unions, try, catch
- Gerenciamento de Memória — Allocators, ponteiros, slices
- Comptime em Zig — Metaprogramação
- Zig Build System — Projetos profissionais
- Testes em Zig — TDD em Zig
📚 Recursos Recomendados
- Zig Documentation
- Ziglings — Exercícios interativos
- Zig by Example
- Discord da Comunidade Zig
Você completou a série “Zig para Iniciantes”! 🎉 Tem dúvidas? Deixe um comentário ou entre na comunidade.
Happy coding! 🦎