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

  1. Prefira for range — mais seguro e idiomático que loops baseados em índice
  2. Evite modificar a coleção durante iteração — especialmente maps, que podem causar comportamento imprevisível
  3. Use context para cancelamento — em loops longos, verifique ctx.Done() periodicamente
  4. Cuidado com closures em goroutines — no Go < 1.22, capture variáveis de loop explicitamente
  5. Prefira break com 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.