ProgramaciónDesarrollador Backend

¿Cómo se implementan los closures en Go, cuáles son las trampas asociadas con su uso al ejecutar goroutines dentro de bucles, y cómo evitar errores típicos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

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.

Pregunta capciosa

¿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

Ejemplos de errores reales debido al desconocimiento de los matices del tema


Historia

En un sistema de análisis de productos, los datos se anonimizaban en goroutines paralelas utilizando un closure que capturaba la variable del bucle. Como resultado, todas las tareas paralelas procesaban el mismo conjunto de datos, lo que llevó a distorsiones estadísticas y reportes incorrectos.

Historia

En un servicio en la nube de integraciones Go, el equipo decidió optimizar la recolección de métricas, ejecutando su procesamiento en un ciclo con funciones anónimas — dentro de la goroutine capturaron el índice del map, el resultado: parte de los manejadores recolectaban datos no para sus servicios, sino para el último índice procesado.

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.