Controle de Fluxo em Zig: if, switch, loops e mais — Guia Completo

Este é o terceiro e último artigo da série “Zig para Iniciantes”. Se você está começando agora, leia primeiro:

  1. Artigo 1: Primeiros Passos
  2. Artigo 2: Sintaxe Básica

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çãoLoop RecomendadoExemplo
Iterar array/sliceforfor (items) |item| { ... }
Contagem conhecidafor (0..n)for (0..10) |i| { ... }
Condição desconhecidawhilewhile (condicao) { ... }
Loop infinitowhile (true)while (true) { ... }
Múltiplas sequênciasfor (a, b)for (nomes, idades)
Precisa de índicefor (.., 0..)for (items, 0..)
Compile-timeinline forinline 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:

📚 Recursos Recomendados


Você completou a série “Zig para Iniciantes”! 🎉 Tem dúvidas? Deixe um comentário ou entre na comunidade.

Happy coding! 🦎

Continue aprendendo Zig

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