O que é For em Go?
A instrução for é o único mecanismo de loop em Go. Diferente de linguagens como Python (que tem for e while) ou JavaScript (que tem for, while, do...while e for...of), Go simplifica radicalmente essa área da sintaxe oferecendo apenas for — mas com versatilidade suficiente para cobrir todos os cenários de iteração.
Essa decisão de design reflete a filosofia minimalista de Go: em vez de múltiplas formas de fazer a mesma coisa, a linguagem oferece uma única construção poderosa. O for em Go pode funcionar como loop clássico com contador, como while de outras linguagens, como loop infinito, e como iterador com range para percorrer coleções como slices, maps e channels.
Se você está aprendendo Go e vem de outra linguagem, dominar o for é um dos primeiros passos essenciais. Ele aparece em praticamente todo programa Go e interage com conceitos fundamentais como goroutines, channels e tratamento de erros.
For Clássico (Três Componentes)
A forma mais tradicional do for em Go é idêntica ao for de C, Java e JavaScript, com três componentes separados por ponto e vírgula: inicialização, condição e pós-execução:
// Sintaxe: for init; condição; pós { corpo }
for i := 0; i < 10; i++ {
fmt.Println(i)
}
A variável declarada na inicialização (i := 0) tem escopo limitado ao bloco do for — ela não existe fora do loop. Os três componentes são opcionais, mas os ponto e vírgulas são obrigatórios quando você usa essa forma:
// Omitindo a inicialização (variável declarada fora)
j := 0
for ; j < 5; j++ {
fmt.Println(j)
}
// Loop decrescente
for i := 100; i >= 0; i -= 10 {
fmt.Printf("Contagem regressiva: %d\n", i)
}
// Múltiplas variáveis (menos comum, mas válido)
for i, j := 0, 10; i < j; i, j = i+1, j-1 {
fmt.Printf("i=%d, j=%d\n", i, j)
}
For como While
Quando você omite a inicialização e o pós-execução, o for funciona como while de outras linguagens — executa enquanto a condição for verdadeira:
// Equivalente a while em outras linguagens
contador := 0
for contador < 100 {
contador += 7
}
fmt.Println("Resultado:", contador) // 105
// Leitura de dados até EOF
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
linha := scanner.Text()
fmt.Println("Li:", linha)
}
Esse estilo é muito comum em Go, especialmente para loops que dependem de condições externas como leitura de I/O, espera por channels ou polling de status:
// Aguardando conexão com retry
var conn net.Conn
var err error
tentativas := 0
for tentativas < 5 {
conn, err = net.Dial("tcp", "localhost:8080")
if err == nil {
break
}
tentativas++
time.Sleep(time.Second * time.Duration(tentativas))
}
Loop Infinito
Omitindo todos os componentes, o for cria um loop infinito. Use break ou return para sair:
// Loop infinito — comum em servidores e workers
for {
msg, ok := <-mensagens
if !ok {
fmt.Println("Channel fechado")
break
}
processar(msg)
}
Loops infinitos são extremamente comuns em Go para implementar servidores, workers de goroutines e processamento contínuo de eventos. O padrão for { select { ... } } é usado em praticamente todo programa concorrente:
func worker(ctx context.Context, tarefas <-chan Tarefa) {
for {
select {
case <-ctx.Done():
fmt.Println("Worker encerrado")
return
case tarefa, ok := <-tarefas:
if !ok {
return
}
executar(tarefa)
}
}
}
Esse padrão combina loop infinito com select e context para criar workers que respondem a sinais de cancelamento — essencial em microserviços e aplicações de produção.
For-Range
A forma for ... range é usada para iterar sobre coleções. O range funciona com slices, arrays, strings, maps e channels:
// Slice
frutas := []string{"maçã", "banana", "laranja"}
for i, fruta := range frutas {
fmt.Printf("%d: %s\n", i, fruta)
}
// Map — ordem de iteração é aleatória
capitais := map[string]string{"BR": "Brasília", "AR": "Buenos Aires"}
for pais, capital := range capitais {
fmt.Printf("%s → %s\n", pais, capital)
}
// String — itera sobre runes (caracteres Unicode)
for i, ch := range "Olá, Go! 🐹" {
fmt.Printf("byte %d: %c (U+%04X)\n", i, ch, ch)
}
// Channel — itera até o channel ser fechado
for msg := range mensagens {
fmt.Println("Recebido:", msg)
}
Variáveis de Loop no Go 1.22+
Antes do Go 1.22, a variável de iteração era reutilizada a cada iteração, causando bugs sutis em closures dentro de goroutines:
// Antes do Go 1.22 — BUG! Todas goroutines imprimem o último valor
for _, url := range urls {
go func() {
fetch(url) // url é compartilhada!
}()
}
// Go 1.22+ — Corrigido! Cada iteração cria nova variável
for _, url := range urls {
go func() {
fetch(url) // url é única por iteração
}()
}
Essa mudança no Go 1.22 foi uma das mais significativas na história da linguagem. Consulte nosso artigo sobre iteradores em Go para entender as implicações dessa mudança.
Break, Continue e Labels
Go suporta break e continue para controlar o fluxo dentro de loops, além de labels para loops aninhados:
// Break — sai do loop
for i := 0; i < 100; i++ {
if i*i > 50 {
fmt.Println("Primeiro quadrado > 50:", i*i)
break
}
}
// Continue — pula para próxima iteração
for i := 0; i < 20; i++ {
if i%3 != 0 {
continue // Pula não-múltiplos de 3
}
fmt.Println(i) // 0, 3, 6, 9, 12, 15, 18
}
Labels para Loops Aninhados
Labels permitem que break e continue afetem um loop externo específico:
externo:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if i+j > 12 {
fmt.Printf("Limite atingido em i=%d, j=%d\n", i, j)
break externo // Sai de ambos os loops
}
}
}
Sem labels, break e continue afetam apenas o loop mais interno. Labels são essenciais quando você precisa sair de loops aninhados, especialmente em lógica de busca e validação.
For com Range Over Functions (Go 1.23+)
A partir do Go 1.23, é possível usar for ... range com funções iteradoras, expandindo significativamente o poder do for:
// Iterador personalizado
func Fibonacci(max int) iter.Seq[int] {
return func(yield func(int) bool) {
a, b := 0, 1
for a <= max {
if !yield(a) {
return
}
a, b = b, a+b
}
}
}
// Uso com for-range
for n := range Fibonacci(100) {
fmt.Println(n) // 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
}
Essa funcionalidade traz um modelo de iteração mais flexível para Go, similar a generators em Python. Para mais detalhes, confira nosso artigo sobre iteradores com range over func.
Padrões Comuns de For em Go
Processar items em lote
itens := obterTodosItens()
lote := 100
for i := 0; i < len(itens); i += lote {
fim := i + lote
if fim > len(itens) {
fim = len(itens)
}
processarLote(itens[i:fim])
}
Retry com backoff exponencial
var resultado *Resposta
var err error
for tentativa := 0; tentativa < 5; tentativa++ {
resultado, err = chamarAPI()
if err == nil {
break
}
espera := time.Second * time.Duration(1<<tentativa)
time.Sleep(espera)
}
Boas Práticas com For
- Prefira
for range— mais seguro e idiomático que loops baseados em índice - Evite modificar a coleção durante iteração — especialmente maps, que podem causar comportamento imprevisível
- Use context para cancelamento — em loops longos, verifique
ctx.Done()periodicamente - Cuidado com closures em goroutines — no Go < 1.22, capture variáveis de loop explicitamente
- Prefira
breakcom label — em loops aninhados, é mais claro que flags booleanas
Perguntas Frequentes (FAQ)
Por que Go tem apenas for e não while ou do-while?
Go foi projetado para ter uma sintaxe minimalista com apenas uma forma de fazer cada coisa. O for de Go é versátil o suficiente para cobrir todos os cenários: for condição {} substitui while, for {} substitui loops infinitos, e for range substitui iteradores. Essa simplificação reduz a carga cognitiva e torna o código mais consistente entre diferentes projetos e equipes.
Como iterar sobre um map em Go?
Use for chave, valor := range meuMap {} para iterar sobre um map. A ordem de iteração é intencionalmente aleatória em Go — se você precisa de ordem determinística, extraia as chaves para um slice, ordene-o com slices.Sort(), e então itere sobre o slice ordenado. Nunca assuma que a ordem de iteração de um map será consistente entre execuções.
O que mudou no for com Go 1.22?
No Go 1.22, a semântica das variáveis de loop mudou para criar uma nova variável a cada iteração, em vez de reutilizar a mesma variável. Essa mudança corrigiu um bug histórico onde goroutines lançadas dentro de loops capturavam a mesma variável, resultando em comportamento inesperado. A mudança é retrocompatível e não afeta código que não usa closures dentro de loops.
Quando usar break com label em Go?
Use break com label quando você tem loops aninhados e precisa sair do loop externo a partir do interno. Sem labels, break afeta apenas o loop mais interno. Labels são comuns em algoritmos de busca, validação de dados tabulares e processamento de estruturas aninhadas. Prefira labels a flags booleanas como encontrado := true, pois são mais explícitas e eficientes.