Historia de la pregunta:
La construcción for-range apareció en Go como una forma de iterar sobre colecciones (matrices, arreglos, maps, cadenas). Los desarrolladores de Go implementaron una optimización: en cada iteración del ciclo se copia el valor, en lugar de usarlo directamente por referencia, lo que puede provocar errores no evidentes, especialmente con las variables del ciclo.
Problema:
Muchos cometen errores al intentar tomar la dirección de una variable dentro de range (por ejemplo, &v), pensando que obtienen la dirección del elemento de la colección, pero en realidad obtienen la dirección de una variable local.
Solución:
En el ciclo for-range, en cada iteración se crean nuevas copias de las variables del iterador (key, value). Para tipos simples esto es inofensivo, pero para estructuras puede llevar a sorpresas al guardar un puntero al elemento: siempre apuntará a la misma variable y no a diferentes elementos de la matriz.
Ejemplo de código:
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // todos los ptrs apuntarán a la misma p }
Características clave:
¿Qué sucederá al guardar referencias a la variable value dentro de range?
Todas las referencias apuntarán a la misma memoria, ya que value es una variable temporal.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // ¡Todos los ptrs contienen una referencia a la misma variable!
¿Se puede modificar un elemento de la colección por referencia a través de value en range?
No, modificar value no afecta al elemento original de la colección. Para modificar, es necesario acceder por índice.
for _, v := range arr { v.Field = 10 // arr no cambiará } for i := range arr { arr[i].Field = 10 // correcto }
¿Garantiza for-range sobre map un orden secuencial de recorrido?
No, el orden de iteración sobre map en Go no está definido y puede ser diferente en cada ejecución de la aplicación.
El desarrollador intenta serializar una lista de referencias a elementos de una estructura a través de range y guarda &value en un slice separado. El resultado es un slice de direcciones iguales.
Ventajas:
Desventajas:
Iteran por índice y guardan un puntero al elemento deseado del arreglo:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Ventajas:
Desventajas: