Estruturas de controle: if, for e switch
Artigo 05 — Estruturas de controle: if, for e switch
Curso: Dominando Go em 1 Ano Prof. Ricardo Matos Módulo 1 — Fundamentos da Linguagem
Controle de fluxo: o esqueleto lógico de qualquer programa
Todo programa útil precisa tomar decisões e repetir operações. As estruturas de controle são os mecanismos que permitem isso. Go possui um conjunto deliberadamente pequeno: if, for e switch. Não existe while, não existe do while, não existe foreach como palavra-chave separada. Tudo é feito com variações do for. Essa simplicidade é intencional e, na prática, suficiente para qualquer situação.
O if em Go
A estrutura básica do if dispensa parênteses em torno da condição — diferente de C, Java e JavaScript — mas exige chaves {} mesmo quando o bloco tem apenas uma linha:
package main
import "fmt"
func main() {
temperatura := 38.5
if temperatura > 37.5 {
fmt.Println("Febre detectada.")
} else if temperatura >= 36.0 {
fmt.Println("Temperatura normal.")
} else {
fmt.Println("Hipotermia possível.")
}
}
Essa obrigatoriedade das chaves elimina uma categoria inteira de bugs famosos em outras linguagens, onde a indentação enganosa sugeria que uma linha fazia parte de um if quando na verdade não fazia.
Inicialização no if
Uma das características mais úteis e idiomáticas do Go é a possibilidade de executar uma instrução de inicialização dentro do próprio if, separada da condição por ponto e vírgula. A variável declarada nessa inicialização existe apenas dentro do escopo do bloco if/else:
package main
import (
"fmt"
"strconv"
)
func main() {
entrada := "42"
if valor, err := strconv.Atoi(entrada); err == nil {
fmt.Println("Conversão bem-sucedida:", valor*2)
} else {
fmt.Println("Erro na conversão:", err)
}
// "valor" e "err" não existem aqui fora
}
Esse padrão é extremamente comum em Go, especialmente ao trabalhar com funções que retornam um resultado e um erro. Ele mantém o escopo das variáveis de erro restrito ao bloco onde são relevantes, evitando poluição do escopo externo.
O for: único laço, múltiplas formas
Go tem apenas a palavra-chave for para todos os tipos de repetição. Ela assume formas diferentes dependendo de como é usada.
Forma clássica com três componentes — equivalente ao for do C:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Forma com apenas a condição — equivalente ao while de outras linguagens:
n := 1
for n < 100 {
n *= 2
}
fmt.Println(n) // 128
Laço infinito — sem condição alguma, equivalente ao while(true):
contador := 0
for {
contador++
if contador >= 3 {
break
}
}
fmt.Println(contador) // 3
for range — para iterar sobre slices, arrays, maps, strings e channels:
linguagens := []string{"Go", "Python", "Rust", "Java"}
for i, lang := range linguagens {
fmt.Printf("%d: %s\n", i, lang)
}
Quando o índice não é necessário, usa-se _:
for _, lang := range linguagens {
fmt.Println(lang)
}
Iterando sobre uma string com range, Go itera sobre os runes, não os bytes — o que significa que caracteres multibyte são tratados corretamente:
for i, r := range "Olá" {
fmt.Printf("índice %d: %c\n", i, r)
}
// índice 0: O
// índice 1: l
// índice 2: á ← ocupa 2 bytes, mas é um único rune
Iterando sobre um map:
capitais := map[string]string{
"Brasil": "Brasília",
"Argentina": "Buenos Aires",
"Chile": "Santiago",
}
for pais, capital := range capitais {
fmt.Printf("%s → %s\n", pais, capital)
}
A ordem de iteração sobre maps em Go é intencionalmente aleatória. O compilador até randomiza a ordem em cada execução para evitar que programadores dependam de uma ordem não garantida.
break e continue
O break interrompe o laço imediatamente. O continue pula para a próxima iteração:
for i := 0; i < 10; i++ {
if i == 3 {
continue // pula o 3
}
if i == 7 {
break // para no 7
}
fmt.Println(i)
}
// Imprime: 0 1 2 4 5 6
Go também suporta labels para break e continue em laços aninhados, permitindo sair de um laço externo a partir de um laço interno:
externo:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break externo // sai do laço externo
}
fmt.Printf("i=%d j=%d\n", i, j)
}
}
O switch em Go
O switch do Go é mais poderoso e mais seguro do que o de C ou Java. A diferença mais importante: não há fall-through automático. Cada case executa apenas seu próprio bloco e para — não é necessário escrever break ao final de cada caso.
package main
import "fmt"
func main() {
dia := 3
switch dia {
case 1:
fmt.Println("Segunda-feira")
case 2:
fmt.Println("Terça-feira")
case 3:
fmt.Println("Quarta-feira")
case 4:
fmt.Println("Quinta-feira")
case 5:
fmt.Println("Sexta-feira")
case 6, 7:
fmt.Println("Final de semana")
default:
fmt.Println("Dia inválido")
}
}
Note que um case pode listar múltiplos valores separados por vírgula, como case 6, 7.
Inicialização no switch
Assim como o if, o switch aceita uma instrução de inicialização:
switch os := runtime.GOOS; os {
case "linux":
fmt.Println("Sistema Linux")
case "darwin":
fmt.Println("Sistema macOS")
default:
fmt.Printf("Outro sistema: %s\n", os)
}
switch sem expressão
Quando o switch não recebe uma expressão, ele se comporta como uma cadeia de if/else if, avaliando cada case como uma expressão booleana independente. Esse padrão é muito mais legível do que um longo encadeamento de if:
nota := 78
switch {
case nota >= 90:
fmt.Println("Conceito A")
case nota >= 80:
fmt.Println("Conceito B")
case nota >= 70:
fmt.Println("Conceito C")
case nota >= 60:
fmt.Println("Conceito D")
default:
fmt.Println("Reprovado")
}
fallthrough explícito
Se o comportamento de queda entre casos for necessário, Go exige que seja declarado explicitamente com a palavra-chave fallthrough. Ela força a execução do próximo case independentemente de sua condição:
x := 1
switch x {
case 1:
fmt.Println("caso 1")
fallthrough
case 2:
fmt.Println("caso 2 — executado por fallthrough")
case 3:
fmt.Println("caso 3 — não executado")
}
// Imprime:
// caso 1
// caso 2 — executado por fallthrough
O fallthrough é raro no código Go idiomático. Seu uso deve ser acompanhado de um comentário explicando a intenção.
defer: execução adiada
Embora não seja estritamente uma estrutura de controle de fluxo, o defer merece introdução aqui por influenciar diretamente a ordem de execução. Uma instrução defer agenda a execução de uma função para o momento em que a função atual retornar — independentemente de como ela retornar, seja por um retorno normal ou por um pânico:
package main
import "fmt"
func main() {
fmt.Println("início")
defer fmt.Println("executado por defer")
fmt.Println("fim")
}
// Saída:
// início
// fim
// executado por defer
Quando múltiplos defer são empilhados, eles executam em ordem LIFO — o último declarado é o primeiro a executar:
for i := 1; i <= 3; i++ {
defer fmt.Println(i)
}
// Imprime: 3, 2, 1
O caso de uso mais comum do defer é garantir o fechamento de recursos como arquivos, conexões de banco de dados ou locks de mutex, logo após sua abertura:
arquivo, err := os.Open("dados.txt")
if err != nil {
log.Fatal(err)
}
defer arquivo.Close() // será executado quando a função retornar
// ... trabalha com o arquivo
Esse padrão garante que o recurso seja sempre liberado, mesmo que a função retorne por um caminho de erro inesperado.
Resumo do que foi coberto
Este artigo percorreu todas as estruturas de controle do Go. O if com inicialização embutida, as quatro formas do for, o switch sem fall-through automático e com expressões booleanas, e o defer para execução adiada. Com esses recursos, é possível expressar qualquer lógica de controle de fluxo de forma clara e idiomática em Go.
Referências e leituras complementares
Especificação da linguagem Go — Statements — Referência formal de todas as estruturas de controle. https://go.dev/ref/spec#Statements
A Tour of Go — Flow control — Seção interativa sobre if, for e switch. https://go.dev/tour/flowcontrol/1
Go by Example: For — Exemplos práticos das formas do laço for. https://gobyexample.com/for
Go by Example: Switch — Exemplos comentados do switch em Go. https://gobyexample.com/switch
Go by Example: Defer — Exemplos e casos de uso do defer. https://gobyexample.com/defer
Effective Go — Control structures — Boas práticas oficiais para uso de estruturas de controle. https://go.dev/doc/effective_go#control-structures
Próximo artigo: Artigo 06 — Funções: declaração, múltiplos retornos e variádicas
you asked
Sim