Fluxo de Controle

Fluxo de Controle

Imagine que você é um chef cozinhando: se a panela está quente, adicione óleo (if); escolha diferentes métodos de cozimento dependendo do ingrediente (switch); continue mexendo até ficar pronto (for); não esqueça de desligar o fogo antes de servir (defer). Os programas funcionam da mesma forma — o fluxo de controle determina o caminho de execução do código, ensinando os computadores como "tomar decisões" e "repetir tarefas".


1. Conceitos Fundamentais

As instruções de fluxo de controle do Go são concisas mas poderosas, incluindo principalmente as seguintes categorias:

1.1 Condicional if / else

A instrução if do Go tem uma característica única: você pode adicionar uma instrução de inicialização antes da condição, separada por ponto e vírgula ;. O escopo da variável é limitado ao bloco if/else.

GO
// Forma básica
if condicao {
    // Executa quando a condição é verdadeira
} else if outraCondicao {
    // Executa quando outra condição é verdadeira
} else {
    // Executa quando nenhuma das anteriores corresponde
}

// Com instrução de inicialização
if instrucaoInicial; condicao {
    // Variável inicializada só é visível dentro do bloco if/else
}

1.2 Instrução switch

O switch do Go tem duas diferenças-chave em relação a outras linguagens:

GO
switch variavel {
case valor1:
    // Trata valor1
case valor2, valor3: // Múltiplos valores separados por vírgula
    // Trata valor2 ou valor3
default:
    // Tratamento padrão
}

1.3 Loop for

for é a única estrutura de loop do Go. Ele substitui while, do-while e outros loops encontrados em outras linguagens.

Forma Sintaxe Caso de Uso
for tradicional for init; cond; post {} Contagem de iteração conhecida
Estilo while for cond {} Loop condicional
Loop infinito for {} Execução contínua
Iteração range for i, v := range colecao {} Iterar coleções

1.4 break e continue

1.5 defer (Execução Adiada)

A instrução defer adia uma chamada de função até que a função circundante retorne. Múltiplas chamadas defer executam em ordem de pilha último a entrar, primeiro a sair (LIFO).

GO
func exemplo() {
    defer fmt.Println("Registrado primeiro, executado por último")
    defer fmt.Println("Registrado segundo, executado penúltimo")
    fmt.Println("Execução normal")
}
// Ordem de saída: Execução normal → Registrado segundo → Registrado primeiro

2. Sintaxe/Uso Básico

Uso do if / else

GO
// if/else padrão
pontuacao := 85
if pontuacao >= 90 {
    fmt.Println("Excelente")
} else if pontuacao >= 80 {
    fmt.Println("Bom")
} else if pontuacao >= 60 {
    fmt.Println("Aprovado")
} else {
    fmt.Println("Reprovado")
}
💡 Dica: Em Go, condições if não precisam de parênteses, mas chaves {} são obrigatórias, e else deve estar na mesma linha que a chave de fechamento do if. Esta é uma regra de sintaxe do Go — não segui-la causará um erro de compilação.

GO
// if com instrução de inicialização
// O escopo de err é limitado ao bloco if/else; não pode ser acessado fora
if err := fazerAlgo(); err != nil {
    fmt.Println("Erro:", err)
}
💡 Dica: if com instruções de inicialização é muito comum em Go, especialmente para tratamento de erros. Ele minimiza o escopo da variável e evita poluir o escopo externo.

Uso do switch

GO
// Switch básico
dia := "Quarta-feira"
switch dia {
case "Segunda-feira":
    fmt.Println("Nova semana começa")
case "Terça-feira", "Quarta-feira", "Quinta-feira":
    fmt.Println("Dia útil"
case "Sexta-feira":
    fmt.Println("Quase fim de semana"
case "Sábado", "Domingo":
    fmt.Println("Feliz fim de semana"
default:
    fmt.Println("Dia inválido"
}
💡 Dica: O switch do Go não requer break após cada case. Se você realmente precisa "cair" para o próximo caso, use a palavra-chave fallthrough, mas isso é raro.

GO
// Switch sem tag (substitui longas cadeias if-else)
pontuacao := 85
switch {
case pontuacao >= 90:
    fmt.Println("Excelente")
case pontuacao >= 80:
    fmt.Println("Bom")
case pontuacao >= 60:
    fmt.Println("Aprovado")
default:
    fmt.Println("Reprovado")
}
💡 Dica: Quando switch não é seguido por uma variável, equivale a switch true e pode substituir longas cadeias if-else para código mais limpo.

Uso do Loop for

GO
// Loop for tradicional
for i := 0; i < 5; i++ {
    fmt.Println(i)
}

// Estilo while
contagem := 0
for contagem < 5 {
    fmt.Println(contagem)
    contagem++
}

// Loop infinito (use com break)
for {
    fmt.Println("Pressione Ctrl+C para sair")
    break // Usando break aqui para evitar um loop infinito real
}

Uso do defer

GO
func lerArquivo(nomeArquivo string) {
    arquivo, err := os.Open(nomeArquivo)
    if err != nil {
        fmt.Println("Falha ao abrir arquivo:", err)
        return
    }
    defer arquivo.Close() // Garante que o arquivo seja fechado quando a função terminar
    // ... ler conteúdo do arquivo ...
}
💡 Dica: Os argumentos do defer são avaliados imediatamente quando a instrução defer aparece, não quando a função executa. Esteja atento a este detalhe para evitar comportamentos inesperados.


Exemplo 1: Uso Básico (Dificuldade ⭐)

Este exemplo demonstra o uso básico de if/else e switch.

GO
package main

import "fmt"

func main() {
    // ===== Exemplo if/else: determinar grau =====
    pontuacao := 78

    // Usando if com instrução de inicialização
    // A variável classificacao só é válida dentro do bloco if/else
    if classificacao := pontuacao / 10; classificacao >= 9 {
        fmt.Printf("Pontuação %d, Grau: Excelente\n", pontuacao)
    } else if classificacao >= 8 {
        fmt.Printf("Pontuação %d, Grau: Bom\n", pontuacao)
    } else if classificacao >= 6 {
        fmt.Printf("Pontuação %d, Grau: Aprovado\n", pontuacao)
    } else {
        fmt.Printf("Pontuação %d, Grau: Reprovado\n", pontuacao)
    }

    // ===== Exemplo switch: determinar estação pelo mês =====
    mes := 8

    switch mes {
    case 3, 4, 5:
        fmt.Printf("Mês %d é Primavera\n", mes)
    case 6, 7, 8:
        fmt.Printf("Mês %d é Verão\n", mes)
    case 9, 10, 11:
        fmt.Printf("Mês %d é Outono\n", mes)
    case 12, 1, 2:
        fmt.Printf("Mês %d é Inverno\n", mes)
    default:
        fmt.Printf("%d não é um mês válido\n", mes)
    }

    // ===== Switch sem tag: substitui cadeia if-else =====
    hora := 14
    switch {
    case hora < 6:
        fmt.Println("Madrugada")
    case hora < 12:
        fmt.Println("Manhã")
    case hora < 18:
        fmt.Println("Tarde")
    default:
        fmt.Println("Noite")
    }
}
▶ Experimente

Saída:

TEXT
Pontuação 78, Grau: Aprovado
Mês 8 é Verão
Tarde

Pontos-Chave:


Exemplo 2: Uso Intermediário (Dificuldade ⭐⭐)

Este exemplo demonstra várias formas de loops for e uso de break / continue.

GO
package main

import "fmt"

func main() {
    // ===== Loop for tradicional: soma de 1 a 100 =====
    soma := 0
    for i := 1; i <= 100; i++ {
        soma += i
    }
    fmt.Printf("Soma de 1 a 100: %d\n", soma)

    // ===== range sobre slice =====
    frutas := []string{"Maçã", "Banana", "Laranja", "Uva"}
    for indice, fruta := range frutas {
        fmt.Printf("Índice %d: %s\n", indice, fruta)
    }

    // ===== range sobre string (por rune) =====
    texto := "Go语言"
    for i, ch := range texto {
        fmt.Printf("Posição %d: %c (Unicode: %U)\n", i, ch, ch)
    }

    // ===== break: encontrar o primeiro número divisível por 7 =====
    for i := 1; i <= 100; i++ {
        if i%7 == 0 {
            fmt.Printf("Primeiro número divisível por 7: %d\n", i)
            break // Sai do loop imediatamente após encontrar
        }
    }

    // ===== continue: imprimir todos os números ímpares de 1-20 =====
    fmt.Print("Números ímpares 1-20: ")
    for i := 1; i <= 20; i++ {
        if i%2 == 0 {
            continue // Pula números pares, vai para a próxima iteração
        }
        fmt.Printf("%d ", i)
    }
    fmt.Println()

    // ===== Break com rótulo: sair do loop externo =====
    fmt.Println("Procurando número 5 em um array 2D:")
    matriz := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }

encontrado: // Nome do rótulo
    for linha, colunas := range matriz {
        for col, val := range colunas {
            if val == 5 {
                fmt.Printf("Encontrado! Posição: linha %d, col %d\n", linha, col)
                break encontrado // Sai do loop externo
            }
        }
    }
}
▶ Experimente

Saída:

TEXT
Soma de 1 a 100: 5050
Índice 0: Maçã
Índice 1: Banana
Índice 2: Laranja
Índice 3: Uva
Posição 0: G (Unicode: U+0047)
Posição 1: o (Unicode: U+006F)
Posição 2: 语 (Unicode: U+8BED)
Posição 5: 言 (Unicode: U+8A00)
Primeiro número divisível por 7: 7
Números ímpares 1-20: 1 3 5 7 9 11 13 15 17 19
Procurando número 5 em um array 2D:
Encontrado! Posição: linha 1, col 1

Pontos-Chave:


Exemplo 3: Aplicação Abrangente (Dificuldade ⭐⭐⭐)

Este exemplo combina todas as instruções de fluxo de controle para implementar um sistema simples de gerenciamento de notas de estudantes.

GO
package main

import "fmt"

// Struct Student
type Student struct {
    Nome    string
    Notas   []int
}

// GetAverage calcula a nota média
func (s Student) GetAverage() float64 {
    if len(s.Notas) == 0 {
        return 0
    }
    total := 0
    for _, nota := range s.Notas {
        total += nota
    }
    return float64(total) / float64(len(s.Notas))
}

// GetGrade retorna o grau com base na nota média
func (s Student) GetGrade() string {
    media := s.GetAverage()
    switch {
    case media >= 90:
        return "A (Excelente)"
    case media >= 80:
        return "B (Bom)"
    case media >= 70:
        return "C (Médio)"
    case media >= 60:
        return "D (Aprovado)"
    default:
        return "F (Reprovado)"
    }
}

// PrintReport imprime o relatório de notas
func PrintReport(students []Student) {
    fmt.Println("========================================")
    fmt.Println("       Relatório de Notas dos Alunos    ")
    fmt.Println("========================================")

    // Usar defer para garantir que o final do relatório seja impresso após todos os dados
    defer fmt.Println("========================================")
    defer fmt.Println("         Relatório Completo")

    for i, student := range students {
        // Usar defer para demonstrar a ordem LIFO
        defer func(nome string, idx int) {
            fmt.Printf("[Limpeza] Processamento do relatório de %s concluído\n", nome)
        }(student.Nome, i)

        // Imprimir informações do estudante
        fmt.Printf("\nAluno %d: %s\n", i+1, student.Nome)
        fmt.Printf("  Notas: %v\n", student.Notas)
        fmt.Printf("  Média: %.1f\n", student.GetAverage())
        fmt.Printf("  Grau: %s\n", student.GetGrade())

        // Analisar nota de cada disciplina
        disciplinas := []string{"Português", "Matemática", "Inglês"}
        for j, nota := range student.Notas {
            // Garantir que o índice está dentro dos limites
            disciplina := "Desconhecida"
            if j < len(disciplinas) {
                disciplina = disciplinas[j]
            }

            // Verificar cada nota de disciplina
            if nota < 60 {
                fmt.Printf("  ⚠ %s (%d) reprovado, precisa de recuperação\n", disciplina, nota)
            }
        }
    }
}

// FindTopStudent encontra o estudante com a maior média
func FindTopStudent(students []Student) (string, float64) {
    if len(students) == 0 {
        return "", 0
    }

    topNome := students[0].Nome
    topMedia := students[0].GetAverage()

    for _, s := range students[1:] {
        if media := s.GetAverage(); media > topMedia {
            topNome = s.Nome
            topMedia = media
        }
    }

    return topNome, topMedia
}

func main() {
    // Criar dados de estudantes
    students := []Student{
        {"Alice", []int{85, 92, 78}},
        {"Bob", []int{90, 95, 88}},
        {"Charlie", []int{72, 58, 65}},
        {"Diana", []int{95, 98, 92}},
    }

    // Imprimir relatório de notas
    PrintReport(students)

    // Encontrar melhor estudante
    topNome, topMedia := FindTopStudent(students)
    fmt.Printf("\n🏆 Melhor Aluno: %s (Média: %.1f)\n", topNome, topMedia)
}
▶ Experimente

Saída:

TEXT
========================================
       Relatório de Notas dos Alunos   
========================================

Aluno 1: Alice
  Notas: [85 92 78]
  Média: 85.0
  Grau: B (Bom)

Aluno 2: Bob
  Notas: [90 95 88]
  Média: 91.0
  Grau: A (Excelente)

Aluno 3: Charlie
  Notas: [72 58 65]
  Média: 65.0
  Grau: D (Aprovado)
  ⚠ Matemática (58) reprovado, precisa de recuperação

Aluno 4: Diana
  Notas: [95 98 92]
  Média: 95.0
  Grau: A (Excelente)
[Limpeza] Processamento do relatório de Diana concluído
[Limpeza] Processamento do relatório de Charlie concluído
[Limpeza] Processamento do relatório de Bob concluído
[Limpeza] Processamento do relatório de Alice concluído
========================================
         Relatório Completo
========================================

🏆 Melhor Aluno: Diana (Média: 95.0)

Pontos-Chave:


3. Casos de Uso Comuns

Caso 1: Cadeia de Tratamento de Erros (if + instrução de inicialização)

Em Go, o uso mais comum de if com instruções de inicialização é o tratamento de erros:

GO
package main

import (
    "fmt"
    "strconv"
)

func main() {
    // Analisar números e tratar erros
    inputs := []string{"42", "abc", "100", "xyz", "0"}

    for _, input := range inputs {
        // Realizar conversão de tipo na inicialização, verificar erro na condição
        if num, err := strconv.Atoi(input); err != nil {
            fmt.Printf("❌ Não é possível analisar %q: %v\n", input, err)
        } else {
            fmt.Printf("✅ Analisado com sucesso: %q → %d\n", input, num)
        }
    }
}

Caso 2: Iterar Map com Filtragem Condicional (for + switch)

GO
package main

import "fmt"

func main() {
    // Tabela de notas dos estudantes
    students := map[string]int{
        "Alice":   85,
        "Bob":     92,
        "Charlie": 58,
        "Diana":   76,
        "Eve":     45,
    }

    // Contar estudantes em cada nível de grau
    excelente, bom, aprovado, reprovado := 0, 0, 0, 0

    for nome, nota := range students {
        switch {
        case nota >= 90:
            excelente++
            fmt.Printf("⭐ %s: %d (Excelente)\n", nome, nota)
        case nota >= 80:
            bom++
            fmt.Printf("👍 %s: %d (Bom)\n", nome, nota)
        case nota >= 60:
            aprovado++
            fmt.Printf("✅ %s: %d (Aprovado)\n", nome, nota)
        default:
            reprovado++
            fmt.Printf("❌ %s: %d (Reprovado)\n", nome, nota)
        }
    }

    fmt.Printf("\nResumo: Excelente %d, Bom %d, Aprovado %d, Reprovado %d\n",
        excelente, bom, aprovado, reprovado)
}

❓ Perguntas Frequentes

P1: Por que o switch do Go não precisa de break?

Os designers do Go acreditavam que o comportamento de fallthrough do switch em C era uma fonte comum de bugs. O switch do Go sai automaticamente após cada caso, prevenindo fallthrough acidental de instruções break esquecidas. Se fallthrough for realmente necessário, você pode usar explicitamente a palavra-chave fallthrough.

GO
// Uso de fallthrough (raro)
x := 1
switch x {
case 1:
    fmt.Println("um")
    fallthrough // Continua executando o código do próximo caso
case 2:
    fmt.Println("dois") // Será executado
default:
    fmt.Println("outro")
}
// Saída: um \n dois

P2: As variáveis em for range são cópias?

Sim. A variável de valor em for range é uma cópia do elemento; modificá-la não afetará a coleção original. Se você precisa modificar elementos na coleção original, acesse-os por índice.

GO
nums := []int{1, 2, 3, 4, 5}

// ❌ Errado: modificando uma cópia
for _, n := range nums {
    n *= 2 // Não afetará nums
}

// ✅ Correto: modificar slice original via índice
for i := range nums {
    nums[i] *= 2 // Modificará nums
}

P3: Quando os argumentos do defer são avaliados?

Os argumentos do defer são avaliados quando a instrução defer aparece, não quando a função retorna.

GO
func main() {
    x := 10
    defer fmt.Println("x no defer:", x) // x é avaliado aqui como 10
    x = 20
    fmt.Println("x após modificação:", x)
}
// Saída:
// x após modificação: 20
// x no defer: 10  (não 20!)

P4: Como capturar corretamente variáveis de loop ao usar goroutines em um loop?

Antes do Go 1.22, variáveis de loop for compartilhavam o mesmo endereço em todas as iterações, o que poderia causar problemas em goroutines. Go 1.22+ corrigiu este comportamento — cada iteração obtém sua própria variável. Se estiver usando uma versão mais antiga, passe a variável como argumento:

GO
// Go 1.22+ — use diretamente
for i := 0; i < 5; i++ {
    go func() {
        fmt.Println(i) // Cada goroutine obtém seu próprio i
    }()
}

// Versões mais antigas — passe explicitamente como argumento
for i := 0; i < 5; i++ {
    go func(n int) {
        fmt.Println(n) // n é uma cópia de i
    }(i)
}

📖 Resumo


📝 Exercícios

Exercício 1 (⭐)

Escreva um programa que receba um ano como entrada e determine se é um ano bissexto. Regras:

GO
package main

import "fmt"

func main() {
    ano := 2024 // Altere este valor para testar

    // Escreva seu código aqui
    // Dica: Use a forma if init; cond
}

Exercício 2 (⭐⭐)

Escreva um programa que use loops for para imprimir uma tabuada. Requisitos de formatação:

GO
package main

import "fmt"

func main() {
    // Escreva seu código aqui
    // Dica: Use dois loops for aninhados
    // Loop externo i de 1 a 9
    // Loop interno j de 1 a i
}

Exercício 3 (⭐⭐⭐)

Escreva um programa que implemente um jogo simples de adivinhação de números:

  1. Use um loop para permitir que o usuário adivinhe repetidamente até acertar
  2. Após cada tentativa, dê uma dica: "muito alto" ou "muito baixo"
  3. Use defer para registrar os horários de início e fim do jogo
  4. Use switch para dar diferentes classificações com base no número de tentativas (1-3: gênio, 4-6: bom, 7+: continue tentando)
GO
package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    // Usar defer para registrar horário de fim
    inicio := time.Now()
    defer func() {
        fmt.Printf("\nDuração do jogo: %v\n", time.Since(inicio))
    }()

    // Gerar número aleatório 1-100
    alvo := rand.Intn(100) + 1
    tentativas := 0

    fmt.Println("=== Jogo de Adivinhação de Números ===")
    fmt.Println("Estou pensando em um número entre 1-100, tente adivinhar!")

    // Escreva seu código aqui
    // Dicas:
    // 1. Use um loop for para manter o jogo rodando
    // 2. Use fmt.Scan para ler a entrada do usuário
    // 3. Use switch para verificar o resultado da tentativa e o nível de classificação
    // 4. Use break para sair do loop quando adivinhar corretamente
}

Próxima Lição

Próxima Lição: Funções → Aprenda definições de funções do Go, múltiplos valores de retorno, parâmetros variádicos, funções anônimas, closures e outros conceitos fundamentais.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%