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.
// 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:
- Break automático: Cada
casetermina automaticamente e não "cai" para o próximo caso. - Suporte a type switch: Você pode verificar o tipo de uma variável.
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
break: Sai imediatamente do loop atual.continue: Pula o código restante na iteração atual e prossegue para a próxima.
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).
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
// 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")
}
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.
// 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)
}
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
// 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"
}
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.
// 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")
}
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
// 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
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 ...
}
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.
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")
}
}
Saída:
Pontuação 78, Grau: Aprovado
Mês 8 é Verão
Tarde
Pontos-Chave:
classificacao := pontuacao / 10é uma instrução de inicialização; o escopo declassificacaoé limitado ao blocoif/elsecase 3, 4, 5doswitchsignifica corresponder a qualquer um de 3, 4 ou 5switchsem tag é adequado para substituir cadeias complexasif-else
Exemplo 2: Uso Intermediário (Dificuldade ⭐⭐)
Este exemplo demonstra várias formas de loops for e uso de break / continue.
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
}
}
}
}
Saída:
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:
for i := 1; i <= 100; i++é o loop padrão de três partesrangesobre um slice retorna índice e valor; sobre uma string itera por caractere Unicode (rune)break encontradousa um rótulo para sair de um loop externo especificado, muito útil em loops aninhados
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.
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)
}
Saída:
========================================
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:
- Múltiplos registros
deferexecutam em ordem LIFO (último a entrar, primeiro a sair): Diana → Charlie → Bob → Alice defer fmt.Println("Relatório Completo")foi registrado primeiro mas executa por último (porque foi registrado mais cedo, é desempilhado por último)switchsem tag substitui cadeias complexasif-elsepara determinação de graufor rangeitera sobre um slice de structs;for i, s := range students[1:]começa a comparar a partir do segundo elementodeferem funções anônimas captura imediatamente os valores dos parâmetros (passa cópias destudent.Nomeei)
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:
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)
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.
// 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.
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.
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 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
if/elsesuporta instruções de inicialização (if init; cond {}); o escopo da variável é limitado ao blocoswitchnão precisa de break por padrão; cada caso termina automaticamente; suporta switch sem tag como alternativa a cadeias if-elseforé o único loop do Go, com quatro formas: for tradicional, estilo while, loop infinito, iteração rangebreakpode ser usado com rótulos para sair de níveis específicos de loop;continuepula a iteração atualdeferexecuta em ordem último a entrar, primeiro a sair (LIFO); argumentos são avaliados imediatamente no momento do deferrangeretorna cópias; modificar cópias não afeta a coleção original
📝 Exercícios
Exercício 1 (⭐)
Escreva um programa que receba um ano como entrada e determine se é um ano bissexto. Regras:
- Um ano é bissexto se for divisível por 4 mas não por 100, ou se for divisível por 400.
- Use a forma
ifcom instrução de inicialização.
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:
- Um multiplicando por linha
- Use tabulação
\tpara alinhamento - Combine loops
fore saída formatada
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:
- Use um loop para permitir que o usuário adivinhe repetidamente até acertar
- Após cada tentativa, dê uma dica: "muito alto" ou "muito baixo"
- Use
deferpara registrar os horários de início e fim do jogo - Use
switchpara dar diferentes classificações com base no número de tentativas (1-3: gênio, 4-6: bom, 7+: continue tentando)
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.



