En Go, los closures son funciones que "cierran" (capturan) variables de su contexto circundante. Con mayor frecuencia, los closures se utilizan para funciones anónimas creadas dentro de otras funciones.
El problema más típico al trabajar con closures es el comportamiento inesperado al usar variables de bucle dentro de una goroutine:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
Cada goroutine puede imprimir el mismo valor i, porque la variable i se incrementa en el bucle, y el closure captura la variable, no su valor en cada iteración.
Forma correcta:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
Este comportamiento se debe a que el closure mantiene una referencia a la variable (su dirección), no a su valor recortado (by value) en cada iteración.
¿Qué valor imprimirán varias goroutines ejecutadas dentro de un bucle si capturan la variable del bucle?
Respuesta: Todas las goroutines pueden imprimir el mismo valor (a menudo el último), ya que ven el valor actual de la variable, y en el momento de la ejecución de la goroutine, el bucle ya ha terminado. Para evitar esto, la variable debe pasarse como parámetro al closure.
Ejemplo:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // probablemente obtendremos: 5 5 5 5 5
Historia
Historia
Historia
En una startup de mensajería, el uso incorrecto de closures al actualizar las coordenadas de un pedido llevaba a que se actualizaran masivamente las coordenadas del último pedido en el slice, en lugar de las actuales — debido a una carrera al acceder al slice dentro de una función anónima.