Pular para o conteúdo

Trilha de Go · parte 5 de 37

Controle de Fluxo Idiomático em Go: Loops, Escopo de Decisão e Type Switches

Os laços for e range modernos (escopo por iteração do Go 1.22+), a inicialização curta em if e switch e os type switches.

Publicada em · 4 min de leitura

Tamanho da fonte:
Largura da coluna:

Controle de Fluxo Idiomático em Go: Loops, Escopo de Decisão e Type Switches

Uma das marcas registradas da filosofia de design do Go é a ortogonalidade e simplicidade sintática: oferecer poucas palavras-chave universais e poderosas, em vez de sobrecarregar a linguagem com dezenas de estruturas redundantes.

Enquanto linguagens como C++, Java ou JavaScript disponibilizam for, while, do-while e until, o Go possui uma única palavra-chave para repetição: o for. Da mesma forma, estruturas condicionais como if e switch possuem recursos exclusivos de inicialização de escopo e avaliação de tipos que eliminam o boilerplate e aumentam a legibilidade do código.


1. Os 4 Formatos do for em Go

O compilador interpreta a instrução for em 4 variações idiomáticas:

package main

import "fmt"

func main() {
	// 1. Loop Clássico de 3 Partes (Inicialização; Condição; Pós-ação)
	for i := 0; i < 5; i++ {
		fmt.Printf("Contagem: %d\n", i)
	}

	// 2. Loop Condicional (O equivalente ao 'while' de outras linguagens)
	count := 10
	for count > 0 {
		count--
	}

	// 3. Loop Infinito (Usado em workers de background e daemons)
	for {
		// Executa continuamente até um 'break' ou 'return'
		break
	}

	// 4. Loop com Range (Iterando sobre slices, arrays, maps ou strings)
	items := []string{"Go", "Rust", "TypeScript"}
	for index, item := range items {
		fmt.Printf("Índice %d: %s\n", index, item)
	}
}

2. A Revolução do Go 1.22: O Fim do Bug de Variável de Loop

Durante mais de uma década (Go $\le$ 1.21), o Go reaproveitava a mesma variável de memória para todas as voltas de um loop for ... range.

Se você disparasse uma goroutine ou criasse um closure dentro do loop capturando essa variável, todas as goroutines acabavam lendo o valor da última iteração, gerando um dos bugs e condições de corrida mais comuns da história da linguagem:

Evolução do Escopo da Variável de Loop no Go 1.22
figura 1 · Evolução do Escopo da Variável de Loop no Go 1.22

O Código Antigo vs Novo:

items := []int{10, 20, 30}

// No Go <= 1.21: Todas as goroutines imprimiam 30, 30, 30!
// Era OBRIGATÓRIO fazer a cópia manual: v := v
for _, v := range items {
    go func() {
        println(v)
    }()
}

// A PARTIR DO GO 1.22+:
// O runtime aloca uma NOVA instância isolada de 'v' em cada iteração!
// O código acima agora imprime 10, 20, 30 com segurança nativa sem gambiarras!

3. A Declaração Curta e Escopo Léxico no if

Em Go, é uma prática universal executar uma função e avaliar seu resultado na mesma linha condicional, limitando o escopo da variável estritamente ao bloco de decisão:

Escopo Léxico de if com Inicialização no Go
figura 2 · Escopo Léxico de if com Inicialização no Go
// A variável 'err' é inicializada e avaliada na mesma instrução
if err := processOrder(order); err != nil {
	log.Printf("Falha ao processar pedido: %v\n", err)
	return err
}

// 🛡️ SEGURANÇA: Aqui fora, a variável 'err' NÃO existe!
// Isso impede que você reutilize acidentalmente um 'err' anterior em validações subsequentes.

Podemos encadear múltiplas declarações com cláusulas else if e else, mantendo a variável viva em toda a árvore de decisão:

if user, err := repo.FindUser(id); err != nil {
	http.Error(w, "Erro de banco", http.StatusInternalServerError)
} else if !user.IsActive {
	http.Error(w, "Usuário desativado", http.StatusForbidden)
} else {
	fmt.Fprintf(w, "Bem-vindo, %s", user.Name)
}

4. O switch Moderno em Go

Diferente de linguagens da família C (como Java e C#), o switch em Go possui melhorias notáveis de ergonomia:

  1. Sem Fallthrough Acidental: O Go insere um break implícito ao final de cada case. Não há perigo de executar o próximo bloco sem querer (a menos que você utilize a palavra-chave explícita fallthrough).
  2. Múltiplas Condições por Case: Você pode agrupar valores separados por vírgula.
  3. Switch Sem Expressão: Substitui cadeias feias de if / else if / else if por uma estrutura limpa e alinhada.

Switch sem Expressão (Tagless Switch)

score := 85

switch {
case score >= 90:
	fmt.Println("Conceito A")
case score >= 80, score >= 70: // Múltiplos valores
	fmt.Println("Conceito B ou C")
case score >= 50:
	fmt.Println("Recuperação")
default:
	fmt.Println("Reprovado")
}

5. Type Switches: Despacho Polimórfico de Interfaces

Em Go, uma interface vazia any (ou interface{}) pode conter qualquer tipo concreto. O Type Switch permite inspecionar o tipo real armazenado em runtime com segurança e sem necessidade de múltiplos type assertions manuais:

package main

import "fmt"

func DescribeValue(i any) {
	// A sintaxe especial i.(type) só é permitida dentro de um switch!
	switch v := i.(type) {
	case int:
		fmt.Printf("É um número inteiro: %d (o dobro é %d)\n", v, v*2)
	case string:
		fmt.Printf("É uma string de tamanho %d: %q\n", len(v), v)
	case bool:
		fmt.Printf("É um booleano com valor: %t\n", v)
	case []int:
		fmt.Printf("É um slice de inteiros com %d elementos\n", len(v))
	default:
		fmt.Printf("Tipo desconhecido não tratado: %T\n", v)
	}
}

6. Resumo Conceitual

  • O Go simplifica a repetição oferecendo apenas a palavra-chave for em 4 variações (clássico, condicional, infinito e range).
  • A partir do Go 1.22, variáveis de loop em for ... range criam uma nova instância por iteração, eliminando o histórico bug de closures e data races.
  • Utilize a sintaxe de inicialização no if (if err := fn(); err != nil) para encapsular variáveis ao seu menor escopo possível.
  • O switch em Go não faz fallthrough por padrão e permite agrupar múltiplos casos com vírgula.
  • Utilize Type Switches (switch v := i.(type)) para extrair tipos concretos de interfaces de forma segura, limpa e idiomática.

↑ Voltar ao topo

Atalhos de teclado

Abrir busca
/
Alternar tema claro/escuro
t
Aula anterior na série
←
Próxima aula na série
→
Lista de atalhos
?