En Go, las funciones anónimas (func literales) son capaces de crear cierres, es decir, acceder a las variables de su ámbito circundante incluso después de que este ha finalizado. Tales cierres asignan memoria en el heap, si es necesario para su correcto funcionamiento (detectado a través del análisis de escape).
Ejemplo:
func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } a := adder() printf("%d ", a(5)) // 5 printf("%d ", a(10)) // 15
Características:
¿Qué mostrará este código?
func main() { fs := []func(){} for i := 0; i < 3; i++ { fs = append(fs, func() { fmt.Println(i) }) } for _, f := range fs { f() } }
Muchos responderán que mostrará 0, 1, 2, sin embargo, el resultado será:
3
3
3
Todos los cierres se refieren a una misma variable i; después de que finaliza el ciclo, su valor es 3.
Correcto: capturar una copia de la variable dentro del cuerpo del ciclo:
for i := 0; i < 3; i++ { v := i // nueva variable fs = append(fs, func() { fmt.Println(v) }) }
Historia
En un proyecto de enrutamiento dinámico, se utilizó un ciclo para crear múltiples handlers a través de closures, cada uno debía capturar su propia ruta. Como resultado, todos los handlers imprimían la última ruta — no se creó una variable separada en cada cierre. El error se descubrió solo durante la integración con la API HTTP.
Historia
Al probar el acceso paralelo a través de goroutines dentro de un ciclo, el closure capturaba la referencia al índice, no a la copia. Esto creaba efectos "aleatorios": los datos no se escribían en su ranura del array, sino en la última.
Historia
En una función de recopilación de estadísticas, el closure modificaba una variable global del ámbito externo, aunque el autor esperaba un contador independiente para cada tarea. El problema se notó debido a la suma inadecuadamente reconstruida, que siempre era global y no privada, a pesar de la lógica local.