O Laço For em Go: Um Guia Visual e Prático
O Go tem uma única palavra-chave de laço, e ela faz tudo. Aprenda o for clássico de três partes, as formas while e infinita, range em slices, strings, maps e inteiros, break e continue, a mudança das variáveis de laço do Go 1.22, armadilhas, medições reais e exercícios.

Nesta página
A maioria das linguagens dá vários laços: for, while, do-while, foreach. O Go dá exatamente uma palavra-chave, o for, e a molda para cada tarefa: contar, repetir enquanto uma condição vale, repetir para sempre até um break e percorrer slices, strings e maps com o range.
Neste guia você vai escrever todas as formas do for do Go, acompanhar um laço passo a passo, conhecer o break, o continue e os rótulos, e cair nas armadilhas clássicas, incluindo um erro que o próprio Go consertou na versão 1.22 e que você vai reproduzir mudando uma única linha do go.mod. Medições reais e exercícios fecham o artigo.
Todas as saídas abaixo vêm de execuções reais com o Go 1.22 no Windows.
O for clássico de três partes
1package main
2
3import "fmt"
4
5func main() {
6 for contagem := 1; contagem <= 5; contagem++ {
7 fmt.Println("Contagem:", contagem)
8 }
9 fmt.Println("Pronto!")
10}1Contagem: 1
2Contagem: 2
3Contagem: 3
4Contagem: 4
5Contagem: 5
6Pronto!
O cabeçalho tem três partes separadas por ponto e vírgula:
- Início (
contagem := 1): roda uma vez, antes de tudo. Normalmente declara a variável do laço. - Condição (
contagem <= 5): conferida antes de cada volta. Verdadeira roda o corpo; falsa termina o laço. - Passo (
contagem++): roda depois de cada volta.
Ao contrário do C, do C++ e do Java, o Go não usa parênteses em volta das três partes, e as chaves são obrigatórias, mesmo para um corpo de uma linha.
Passo a passo

A condição é conferida 6 vezes para 5 voltas: a última conferência (6 <= 5) é a que para o laço. E a contagem só existe dentro do laço; usá-la depois da chave de fechamento é um erro de compilação (undefined: contagem).
Contando de outros jeitos
O passo pode fazer qualquer coisa, não só somar um:
1package main
2
3import "fmt"
4
5func main() {
6 for regressiva := 5; regressiva > 0; regressiva-- {
7 fmt.Print(regressiva, " ")
8 }
9 fmt.Println("Decolar!")
10
11 for par := 0; par <= 10; par += 2 {
12 fmt.Print(par, " ")
13 }
14 fmt.Println()
15
16 for potencia := 1; potencia < 1000; potencia *= 2 {
17 fmt.Print(potencia, " ")
18 }
19 fmt.Println()
20}5 4 3 2 1 Decolar!
0 2 4 6 8 10
1 2 4 8 16 32 64 128 256 512 Uma palavra-chave, quatro formas

Só a condição: o while do Go
Tire o início e o passo, e o for vira um laço while ("enquanto"):
1package main
2
3import "fmt"
4
5func main() {
6 poupanca := 100.0
7 anos := 0
8 for poupanca < 200 {
9 poupanca *= 1.07
10 anos++
11 }
12 fmt.Printf("Dobrou para %.2f depois de %d anos a 7%%\n", poupanca, anos)
13}Dobrou para 210.49 depois de 11 anos a 7%Use essa forma quando você não sabe de antemão quantas voltas vai precisar.
Sem condição: repita até o break
O for { } repete para sempre. Você sai com break (ou return):
1package main
2
3import "fmt"
4
5func main() {
6 numero := 27
7 passos := 0
8 for {
9 if numero == 1 {
10 break
11 }
12 if numero%2 == 0 {
13 numero /= 2
14 } else {
15 numero = 3*numero + 1
16 }
17 passos++
18 }
19 fmt.Println("Chegou a 1 depois de", passos, "passos")
20}Chegou a 1 depois de 111 passosEssa é a famosa sequência de Collatz: divida os pares por dois, multiplique os ímpares por três e some um, e (até onde alguém já conferiu) você sempre chega a 1. Começar do 27 leva surpreendentes 111 passos. Um for { } também é como servidores e jogos rodam os seus laços principais.
range: percorrendo coleções
Slices
1package main
2
3import "fmt"
4
5func main() {
6 frutas := []string{"maçã", "banana", "cereja"}
7 for indice, fruta := range frutas {
8 fmt.Println(indice, fruta)
9 }
10 for _, fruta := range frutas {
11 fmt.Print(fruta, " ")
12 }
13 fmt.Println()
14 for indice := range frutas {
15 fmt.Print(indice, " ")
16 }
17 fmt.Println()
18}10 maçã
21 banana
32 cereja
4maçã banana cereja
50 1 2 O range dá dois valores por volta: o índice e uma cópia do elemento. Não precisa do índice? Escreva _ (o identificador vazio), porque uma variável sem uso é erro de compilação. Precisa só do índice? Deixe a segunda variável de fora.
Inteiros (Go 1.22)
Desde o Go 1.22, o range funciona com um inteiro simples, contando do 0 até ele (sem incluí-lo):
1package main
2
3import "fmt"
4
5func main() {
6 for volta := range 3 {
7 fmt.Println("Volta", volta+1)
8 }
9}Volta 1
Volta 2
Volta 3for volta := range 3 é um jeito mais limpo de escrever for volta := 0; volta < 3; volta++.
Strings: caracteres, não bytes
1package main
2
3import "fmt"
4
5func main() {
6 for indice, caractere := range "Olá!" {
7 fmt.Printf("byte %d: %c\n", indice, caractere)
8 }
9}1byte 0: O
2byte 1: l
3byte 2: á
4byte 4: !![O range numa string. A string "Olá!" tem 5 bytes em UTF-8: O no byte 0, l no byte 1, á ocupando os bytes 2 e 3, e ! no byte 4. Um laço for range entrega um rune, um caractere inteiro, por volta, junto com o índice do seu primeiro byte: 0 O, 1 l, 2 á, 4 !. O índice pula de 2 para 4 porque o á tem dois bytes. Um laço clássico com s[i] visitaria 5 bytes e partiria o á ao meio](https://nbkmundotech.us-southeast-1.linodeobjects.com/static/images/cccc2e69-050e-4bb0-b54b-dcc35cecc5bb.png?v=1792884662152)
O range numa string entrega um rune (um caractere Unicode inteiro) por volta, mais o índice do byte onde ele começa. Por isso o índice pula de 2 para 4: o á ocupa dois bytes. (O %c imprime um rune como caractere.) Um laço de índice clássico com texto[indice] visitaria bytes e cortaria o á ao meio, algo que importa muito em português.
Maps: em ordem nenhuma
1package main
2
3import (
4 "fmt"
5 "sort"
6)
7
8func main() {
9 capitais := map[string]string{"França": "Paris", "Japão": "Tóquio", "Brasil": "Brasília", "Quênia": "Nairóbi"}
10
11 paises := make([]string, 0, len(capitais))
12 for pais := range capitais {
13 paises = append(paises, pais)
14 }
15 sort.Strings(paises)
16
17 for _, pais := range paises {
18 fmt.Println(pais, "→", capitais[pais])
19 }
20}1Brasil → Brasília
2França → Paris
3Japão → Tóquio
4Quênia → NairóbiO range num map entrega cada chave e valor, mas a ordem é aleatória de propósito, para os programas não passarem a depender dela. Um simples for pais, capital := range capitais { fmt.Println(pais, capital) } imprimiu isto em duas execuções seguidas:
1$ go run .
2Brasil Brasília
3Quênia Nairóbi
4França Paris
5Japão Tóquio
6$ go run .
7Japão Tóquio
8Brasil Brasília
9Quênia Nairóbi
10França Paris
Percorremos esse map 10.000 vezes e contamos as ordens: só apareceram 4 ordens diferentes (rotações de uma mesma sequência, porque o Go escolhe um ponto de partida aleatório), e uma delas saiu em 62% das vezes. De qualquer jeito: quando a ordem importar, junte as chaves e ordene, como acima.
break, continue e rótulos
1package main
2
3import "fmt"
4
5func main() {
6 for numero := 1; numero <= 10; numero++ {
7 if numero%3 == 0 {
8 continue
9 }
10 if numero > 8 {
11 break
12 }
13 fmt.Print(numero, " ")
14 }
15 fmt.Println()
16
17 alvo := 12
18busca:
19 for primeiro := 1; primeiro <= 6; primeiro++ {
20 for segundo := 1; segundo <= 6; segundo++ {
21 if primeiro*segundo == alvo {
22 fmt.Printf("%d x %d = %d\n", primeiro, segundo, alvo)
23 break busca
24 }
25 }
26 }
27}1 2 4 5 7 8
2 x 6 = 12continuepula o resto desta volta e segue para a próxima (o 3 e o 6 são pulados).breaksai do laço de vez (no 9).- Rótulos: um
breaksimples só sai do laço mais de dentro. Coloque um rótulo comobusca:antes do laço de fora, e obreak buscasai dos dois de uma vez, então paramos no primeiro par (2 × 6) em vez de achar também 3 × 4, 4 × 3 e 6 × 2.
Um laço ou uma fórmula?
Laços são o jeito óbvio de somar 1 + 2 + … + n:
1package main
2
3import "fmt"
4
5func main() {
6 limite := 100
7 total := 0
8 for numero := 1; numero <= limite; numero++ {
9 total += numero
10 }
11 fmt.Println("Laço:", total)
12 fmt.Println("Fórmula:", limite*(limite+1)/2)
13}Laço: 5050
Fórmula: 5050Ainda menino, Carl Friedrich Gauss teria descoberto o atalho: junte 1 com 100, 2 com 99 e assim por diante, 50 pares de 101. Medimos os dois para n = 10 milhões:

| O quê | Tempo |
|---|---|
| Somar 10 milhões de itens de um slice, laço de índice | 5,2 ms |
Somar 10 milhões de itens de um slice, laço com range | 5,6 ms |
| Somar de 1 a 10 milhões com um laço | 3,9 ms |
| Somar de 1 a 10 milhões com a fórmula | 0,7 ns |
Duas lições. Laços de índice e com range têm a mesma velocidade (o que rodávamos primeiro saía na frente, então a diferença é ruído), então escolha o mais claro, normalmente o range. E um laço que gasta meio nanossegundo por número é rápido, mas uma ideia melhor é milhões de vezes mais rápida.
Armadilhas
Armadilha 1: o range entrega uma cópia
1package main
2
3import "fmt"
4
5func main() {
6 precos := []int{10, 20, 30}
7 for _, preco := range precos {
8 preco *= 2
9 }
10 fmt.Println(precos)
11
12 for indice := range precos {
13 precos[indice] *= 2
14 }
15 fmt.Println(precos)
16}[10 20 30]
[20 40 60]A variável preco é uma cópia de cada elemento, então mudá-la não muda nada no slice. Para modificar os elementos, atribua pelo índice.
Armadilha 2: variáveis de laço em closures (consertado no Go 1.22)
Este programa guarda três funções num laço e as chama depois:
1package main
2
3import "fmt"
4
5func main() {
6 var impressoras []func()
7 for i := 0; i < 3; i++ {
8 impressoras = append(impressoras, func() { fmt.Print(i, " ") })
9 }
10 for _, impressora := range impressoras {
11 impressora()
12 }
13 fmt.Println()
14}0 1 2 É o que você esperaria, e é o que o Go 1.22 em diante imprime. Mas a linha go do seu go.mod decide quais regras de laço valem. Troque de go 1.22 para go 1.21 e rode exatamente o mesmo código:
$ go run .
3 3 3 
Antes do Go 1.22, o laço tinha um único i compartilhado por todas as voltas, então as três funções viam o valor final dele, 3. O mesmo erro atingia goroutines iniciadas dentro de laços, e era um dos enganos mais comuns em código Go, então o Go 1.22 mudou as regras: cada volta agora ganha a sua própria variável. (O i aqui é o contador simples de um exemplo curto; em laços mais longos, prefira nomes descritivos.) Se você mantém código antigo, confira a versão no go.mod dele.
Armadilha 3: um a mais ou a menos
for indice := 0; indice <= len(frutas); indice++ dá uma volta a mais e quebra com panic: runtime error: index out of range [3] with length 3 ("índice fora do intervalo"). Os índices válidos vão de 0 a len - 1, então a condição é <, não <=. Melhor ainda: use o range e deixe o Go cuidar dos limites.
Sua vez: pratique
Tente fazer estes antes de espiar as respostas. Rode cada um com go run ..
Exercício 1: Adivinhe a saída (aquecimento). O que isto imprime?
1package main
2
3import "fmt"
4
5func main() {
6 for numero := 10; numero > 0; numero -= 3 {
7 fmt.Print(numero, " ")
8 }
9 fmt.Println()
10 for indice, letra := range "Go!" {
11 fmt.Print(indice, string(letra), " ")
12 }
13 fmt.Println()
14}Exercício 2: Tabuada. Imprima a tabuada do 7, de 7 × 1 a 7 × 10, uma linha cada, como 7 x 3 = 21.
Exercício 3: Some os pares. Some os números pares de 1 a 100 usando continue para pular os ímpares.
Exercício 4: FizzBuzz. Para os números de 1 a 15, imprima Fizz para múltiplos de 3, Buzz para múltiplos de 5, FizzBuzz para múltiplos dos dois e o número nos outros casos, tudo numa linha.
Exercício 5: Conte as vogais (desafio). Conte as vogais (a, e, i, o, u, incluindo as acentuadas como á e ê) em "A programação é divertida" usando range na string e strings.ContainsRune.
Quiz rápido
- Quantas palavras-chave de laço o Go tem?
- Como se escreve um laço
whileem Go? - Que dois valores o
rangeentrega num slice? - Por que o
rangeem"Olá"pula do índice 2 para o 4? - O que mudou nas variáveis de laço no Go 1.22?
Respostas
Exercício 1: O primeiro laço conta de 3 em 3 para baixo até o número não ser mais maior que 0; o segundo entrega índices de bytes e caracteres:
1package main
2
3import "fmt"
4
5func main() {
6 for numero := 10; numero > 0; numero -= 3 {
7 fmt.Print(numero, " ")
8 }
9 fmt.Println()
10 for indice, letra := range "Go!" {
11 fmt.Print(indice, string(letra), " ")
12 }
13 fmt.Println()
14}10 7 4 1
0G 1o 2! Exercício 2: O range 10 conta de 0 a 9, então some 1:
1package main
2
3import "fmt"
4
5func main() {
6 for passo := range 10 {
7 multiplicador := passo + 1
8 fmt.Printf("7 x %d = %d\n", multiplicador, 7*multiplicador)
9 }
10}17 x 1 = 7
27 x 2 = 14
37 x 3 = 21
47 x 4 = 28
57 x 5 = 35
67 x 6 = 42
77 x 7 = 49
87 x 8 = 56
97 x 9 = 63
107 x 10 = 70Exercício 3: O continue pula os ímpares antes de serem somados:
1package main
2
3import "fmt"
4
5func main() {
6 total := 0
7 for numero := 1; numero <= 100; numero++ {
8 if numero%2 != 0 {
9 continue
10 }
11 total += numero
12 }
13 fmt.Println(total)
14}2550Exercício 4: Confira "os dois" primeiro, senão o 15 pararia no Fizz:
1package main
2
3import "fmt"
4
5func main() {
6 for numero := 1; numero <= 15; numero++ {
7 switch {
8 case numero%15 == 0:
9 fmt.Print("FizzBuzz ")
10 case numero%3 == 0:
11 fmt.Print("Fizz ")
12 case numero%5 == 0:
13 fmt.Print("Buzz ")
14 default:
15 fmt.Print(numero, " ")
16 }
17 }
18 fmt.Println()
19}1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz (Um switch sem valor confere cada condição de case em ordem, um substituto arrumado para uma corrente de if/else if.)
Exercício 5: O range na string entrega caracteres inteiros, então o á e o é contam uma vez cada:
1package main
2
3import (
4 "fmt"
5 "strings"
6)
7
8func main() {
9 texto := "A programação é divertida"
10 vogais := "aeiouáéíóúâêôãõAEIOU"
11 contagem := 0
12 for _, caractere := range texto {
13 if strings.ContainsRune(vogais, caractere) {
14 contagem++
15 }
16 }
17 fmt.Println("Vogais:", contagem)
18}Vogais: 11Quiz: (1) Uma: for. (2) Só com a condição: for total < 100 { … }. (3) O índice e uma cópia do elemento. (4) O range numa string entrega runes com o índice do byte onde começam, e o á ocupa dois bytes. (5) Cada volta do laço agora ganha a sua própria cópia da variável, então closures e goroutines criadas no laço veem o valor da sua própria volta (quando o go.mod diz 1.22 ou mais novo).
Cola rápida
| Eu quero… | Escreva |
|---|---|
| Contar de 1 a 10 | for contagem := 1; contagem <= 10; contagem++ { } |
| Repetir n vezes (Go 1.22+) | for passo := range n { } |
| Repetir enquanto uma condição vale | for total < 100 { } |
Repetir até um break | for { … if pronto { break } … } |
| Percorrer um slice | for indice, valor := range itens { } |
| Só valores / só índices | for _, valor := range itens / for indice := range itens |
| Percorrer os caracteres de uma string | for indice, caractere := range texto { } |
| Percorrer um map (ordem qualquer) | for chave, valor := range tabela { } |
| Pular para a próxima volta | continue |
| Sair do laço | break |
| Sair de laços aninhados | rotule o laço de fora e use break rotulo |
| Mudar elementos de um slice | itens[indice] = …, e não a cópia do range |
Resumindo
A única palavra-chave for do Go cobre todo tipo de laço:
- Três partes (
início; condição; passo) para contar, sem parênteses e com chaves obrigatórias. - Só a condição é o
whiledo Go; sem condição repete até umbreak. - O
rangepercorre slices (índice e uma cópia), strings (runes e índices de bytes), maps (em ordem aleatória) e, desde o Go 1.22, inteiros. - O
continuepula uma volta, obreaksai, e os rótulos saem de laços aninhados. - Desde o Go 1.22, cada volta ganha a sua própria variável de laço, consertando o clássico erro das closures (o mesmo código imprimiu
3 3 3comgo 1.21nogo.mod). - Laços de índice e com
rangesão igualmente rápidos, cerca de meio nanossegundo por elemento no nosso teste, mas a fórmula certa venceu o laço por milhões de vezes.
Voltando pela série: Como Definir uma Variável em Go, Como Instalar o Go no Linux e Seu Primeiro Programa em Go. A seguir: slices de duas dimensões em Go.
Leia a seguir

O Que É a Linguagem de Programação Rust? Um Guia Simples da Linguagem Rápida Que Não Deixa Você Travar
Um guia simples e sem jargões sobre o Rust, a linguagem tão rápida quanto o C que impede famílias inteiras de erros de memória antes de o programa rodar. Entenda a ideia de posse, por que gigantes da tecnologia o adotam e onde ele é usado.

O Que É a Linguagem de Programação Go? Um Guia Simples da Linguagem da Nuvem
Um guia simples e sem jargões sobre o Go, a linguagem simples e rápida que o Google criou para a nuvem. Entenda o que são goroutines, por que um programa Go é um arquivo só e como o Go passou a mover o Docker, o Kubernetes e boa parte da internet.

O Que É a Linguagem de Programação Ruby? Um Guia Simples da Linguagem Feita para a Felicidade
Um guia simples e sem jargões sobre o Ruby, a linguagem de programação feita para deixar os programadores felizes. Entenda por que o código Ruby parece inglês, como o Ruby on Rails move sites como GitHub e Shopify, e onde o Ruby brilha.
Comentários
Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.