Go tem exatamente uma palavra-chave de laço: for. Ela tem quatro formatos, e juntos eles cobrem todo laço que você escreveria com for, while, do while ou foreach em outras linguagens.
Não há parênteses em volta do cabeçalho do laço, e as chaves são sempre obrigatórias.
O laço de três cláusulas
for init; condition; post {
// body
}
initexecuta uma vez antes do laço. Normalmente é uma declaração curta de variável comoi := 0, e essa variável só existe dentro do laço.conditioné verificada antes de cada iteração. Quando é falsa, o laço termina.- O corpo executa.
postexecuta depois de cada iteração, normalmentei++.
i++ é uma instrução em Go, não uma expressão, então post não pode conter i++, j--. Use uma atribuição paralela: i, j = i+1, j-1.
As três cláusulas são opcionais. for ; i < 10; {} é válido, e o gofmt o reescreve como for i < 10 {}, que é a próxima forma.
Laço só com condição (o while do Go)
Só com uma condição, o for se comporta como o while de outras linguagens:
Não existe um do while separado. A página do laço while mostra como obter o comportamento de "executar pelo menos uma vez".
Laços infinitos
Um for sem nada depois dele repete para sempre. Saia com break, return, os.Exit ou um panic:
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
process(line)
}
Esse é o formato padrão para servidores, workers e laços de entrada. Um for {} com corpo vazio queima um núcleo de CPU; em código real, o corpo bloqueia em alguma coisa (uma leitura, um recebimento de channel, um select).
Laços com range
range percorre uma coleção e entrega um ou dois valores por iteração. O que eles são depende do que você percorre:
| Percorrendo | Primeiro valor | Segundo valor |
|---|---|---|
| slice ou array | índice | elemento (uma cópia) |
| string | índice em bytes | rune (caractere Unicode) |
| map | chave | valor |
| channel | elemento | (nenhum) |
inteiro n (Go 1.22) | de 0 a n menos 1 | (nenhum) |
| função iteradora (Go 1.23) | o que a função entrega | o que a função entrega |
Três detalhes que a saída mostra:
- A variável de valor é uma cópia.
v *= 2dentro do laço não mudaria o slice; para alterar elementos, acesse o slice pelo índice comnums[i], como faz o terceiro laço. - O índice de uma string é um offset em bytes.
éocupa dois bytes, então o índice pula de 1 para 3. - A ordem do map é aleatória de propósito. Go a embaralha para que o código não possa depender dela.
slices.Sorted(maps.Keys(m))(Go 1.23) entrega as chaves em ordem.
O range é explicado por completo, incluindo channels, funções iteradoras e as regras de cópia, na página de range.
break e continue
break encerra o laço mais interno. continue pula para a próxima iteração (executando o post antes, em um laço de três cláusulas):
break e continue com label
Um break simples só sai do laço mais interno. Para sair de um laço externo a partir de um laço aninhado, dê um label ao laço externo:
Um label é um identificador seguido de dois-pontos, colocado logo antes do for. Go trata um label sem uso como erro de compilação, igual a uma variável sem uso.
break dentro de switch e select
Dentro de um for, um break em um case de switch ou select sai do switch ou do select, não do laço:
for {
switch cmd := next(); cmd {
case "quit":
break // only exits the switch; the loop keeps running
}
}
Use um break com label, ou return, para sair do laço de dentro de um switch ou select.
Variáveis de laço desde o Go 1.22
O Go 1.22 mudou o escopo das variáveis de laço: agora cada iteração recebe uma cópia nova de i e v. Antes, uma única variável era reaproveitada no laço inteiro, o que quebrava closures e goroutines que a capturavam:
A partir do Go 1.22 isto imprime 0 1 2 e [aa bb cc]. Na semântica antiga, o mesmo programa escrito com for i := 0; i < 3; i++ (range sobre inteiro não existia antes do 1.22) imprimia 3 3 3, e as goroutines disputavam os i e name compartilhados. O antigo contorno, i := i no topo do corpo do laço, não é mais necessário.
O comportamento é escolhido por módulo, pela linha go do go.mod, e não pela versão do compilador. Um módulo que ainda diz go 1.21 mantém a semântica antiga mesmo quando compilado com o Go 1.24.
Erros comuns
Alterar a cópia. for _, v := range items { v.Count++ } altera uma cópia. Use items[i].Count++, ou percorra um slice de ponteiros.
Fazer append durante o range. O range avalia o slice uma vez, no início. Elementos acrescentados dentro do laço não são visitados, então o laço ainda termina, mas não vê os itens novos.
Remover de um slice pelo índice em um laço para a frente. Remover o elemento i desloca o resto para a esquerda, e o próximo elemento é pulado. Monte um slice novo, percorra de trás para a frente ou use slices.DeleteFunc.
Erro de um a mais com <=. for i := 0; i <= len(s); i++ lê s[len(s)] na última passada e causa panic com index out of range. Use <, ou melhor, range.
Trabalho pesado na condição. A condição executa a cada iteração. for i := 0; i < expensiveCount(); i++ chama a função toda vez; calcule o valor uma vez antes do laço.
Usar um float como contador. for x := 0.0; x != 1.0; x += 0.1 nunca termina, porque 0.1 não tem representação binária exata. Conte com um inteiro e calcule o float a partir dele.
Perguntas frequentes
Como escrever um laço for em Go?
A forma clássica tem três partes separadas por ponto e vírgula e nenhum parêntese:
for i := 0; i < 5; i++ {
fmt.Println(i)
}
Desde o Go 1.22 você também pode escrever for i := range 5, que conta de 0 a 4. Para percorrer um slice, use for i, v := range items.
Go tem laço while?
Não existe a palavra-chave while, mas um for só com condição é um laço while: for n > 0 { n /= 2 }. for { ... } sem condição é um laço infinito do qual você sai com break ou return.
Como percorrer um map em Go?
for key, value := range m { ... }. A ordem de iteração não é especificada, e o runtime a embaralha, então ela pode mudar de um laço para o outro. Nunca dependa dela. Para uma ordem estável, junte as chaves, ordene com slices.Sort e percorra as chaves ordenadas.
Como sair de um laço aninhado em Go?
Coloque um label antes do laço externo e cite-o no break: outer: for ... { for ... { if found { break outer } } }. Um break simples só sai do for, switch ou select mais interno. continue outer, da mesma forma, pula para a próxima iteração do laço com label.
O que mudou nas variáveis de laço no Go 1.22?
Antes do Go 1.22, um laço for tinha uma única variável compartilhada por todas as iterações, então closures e goroutines iniciadas no laço muitas vezes viam todas o valor final. Desde o Go 1.22 cada iteração ganha a sua própria cópia, então capturar i ou v em uma closure funciona como esperado. O novo comportamento vale quando o go.mod do módulo diz go 1.22 ou mais recente.