Funções anônimas
Um literal de função é uma função sem nome. Ele é uma expressão, então pode ir em qualquer lugar onde cabe um valor: uma variável, um argumento, um valor de retorno, um campo de struct.
Go não tem atalho de arrow function ou lambda. A forma completa func(params) results { body } é o único jeito de escrever uma.
Capturando variáveis
Um literal de função pode usar variáveis da função que o envolve. Ele não as copia: a closure e o código ao redor compartilham uma única variável.
A variável capturada vive enquanto alguma closure ainda se referir a ela, mesmo depois que a função que a declarou retornou. O compilador a move para o heap quando necessário; você nunca gerencia isso.
Geradores e contadores
Devolver uma closure de uma função dá a cada função devolvida o seu próprio estado privado.
Cada chamada a counter() cria um n novo, então c1 e c2 contam de forma independente. Nada do lado de fora consegue ler ou zerar n, o que faz disso uma pequena forma de encapsulamento.
Closures em laços (Go 1.22 em diante)
Antes do Go 1.22, um laço for tinha uma única variável de laço para o laço inteiro. As closures criadas dentro dele capturavam todas essa mesma variável e viam o seu valor final. Esse era o bug de closure mais comum em Go.
Desde o Go 1.22, cada iteração ganha a sua própria cópia, então o código abaixo imprime 0 1 2:
No Go 1.21 e anteriores, o mesmo laço escrito como for i := 0; i < 3; i++ (range sobre inteiro também é novidade do 1.22) imprimia 3 3 3. A nova regra vale por módulo, com base na linha go do go.mod, então um módulo antigo continua com o comportamento antigo até que a sua linha go seja elevada para 1.22 ou mais. Você ainda vai ver i := i ou v := v dentro de laços em código antigo: era a correção manual e agora é redundante.
A regra por iteração só cobre variáveis declaradas pela própria instrução for. Uma variável declarada antes do laço e atualizada no corpo continua sendo uma única variável compartilhada:
var last string
for _, s := range items {
last = s
handlers = append(handlers, func() { use(last) }) // every closure sees the final value
}
Closures e goroutines
Uma goroutine iniciada com um literal de função também é uma closure. As mesmas regras de compartilhamento valem, mais a regra de sempre da concorrência: se várias goroutines escrevem em uma variável capturada, você precisa de um mutex ou de um channel.
Isto sempre imprime 5050. Cada goroutine lê o seu próprio i (semântica de laço do Go 1.22) e escreve no total compartilhado sob um lock. Tire o mutex e o resultado fica imprevisível; go run -race reporta uma data race. Veja goroutines para WaitGroup e o detector de race.
Onde closures aparecem em código real
- Ordenação e busca:
slices.SortFunc,slices.IndexFuncesort.Slicerecebem um literal de função. - Limpeza adiada:
defer func() { ... }()executa um bloco na saída da função e pode ler as variáveis dela. Veja defer. - Middleware HTTP: uma função que recebe um handler e devolve um novo literal
http.HandlerFuncque o envolve. - Configuração: functional options (
func WithTimeout(d time.Duration) Option { return func(c *Config) { c.Timeout = d } }) são closures sobre o argumento.
Um exemplo de middleware que captura tanto um parâmetro quanto um contador:
Erros comuns
- Esperar um retrato do valor. Uma closure lê o valor atual da variável quando executa, não o valor de quando foi criada. Passe o valor como parâmetro se precisar de um retrato:
go func(v int) { ... }(x). - Literais recursivos. Um literal de função não pode se referir a si mesmo pela variável a que está sendo atribuído no mesmo
:=. Declare a variável antes:var walk func(n int); walk = func(n int) { ... walk(n-1) }. - Escritas sem sincronização a partir de goroutines. Capturar não é sincronizar. Proteja as escritas compartilhadas.
Perguntas frequentes
O que é uma função anônima em Go?
Um literal de função sem nome: func(x int) int { return x * 2 }. Você pode atribuí-la a uma variável, passá-la como argumento, devolvê-la ou chamá-la na hora acrescentando () depois da chave de fechamento. Go não tem uma sintaxe de lambda separada; o literal de função é a lambda.
O que é uma closure em Go?
Um literal de função que se refere a variáveis declaradas fora dele. A função mantém essas variáveis vivas e as compartilha com o escopo que a envolve, então mudanças feitas dentro da closure ficam visíveis fora, e vice-versa. As variáveis são capturadas por referência, não copiadas.
Closures em Go capturam variáveis por valor ou por referência?
Por referência. A closure e o código ao redor usam a mesma variável. Se você precisa de um retrato do valor, copie-o para uma variável nova antes de criar a closure, ou passe-o como argumento.
O bug da variável de laço em closures foi corrigido no Go?
Sim, desde o Go 1.22. Cada iteração de um laço for agora declara uma variável de laço nova, então closures e goroutines criadas no laço veem o valor daquela iteração. Vale para módulos cujo go.mod diz go 1.22 ou mais recente. Código antigo muitas vezes tem i := i dentro do laço como contorno; isso não faz mal, mas não é mais necessário.