Historia de la pregunta:
La construcción for-range apareció en Go como una forma de iterar sobre colecciones (slices, arrays, maps, cadenas). Los desarrolladores de Go implementaron una optimización: en cada iteración del bucle se produce una copia del valor, en lugar de usarlo directamente por referencia, lo que puede llevar a errores poco evidentes, especialmente con las variables de bucle.
Problema:
Muchos se equivocan al intentar obtener la dirección de una variable dentro de range (por ejemplo, &v), creyendo 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 bucle for-range se crean nuevas copias de las variables del iterador (key, value) en cada iteración. Para tipos simples esto no tiene consecuencias, pero para estructuras puede llevar a sorpresas al guardar un puntero al elemento: siempre apuntará a la misma variable, y no a diferentes elementos del slice.
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 a través de la referencia value en range?
No, modificar value no afecta al elemento original de la colección. Para modificarlo, 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 en la iteración?
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.
Un desarrollador intenta serializar una lista de referencias a elementos de una estructura a través de range y guarda &value en un slice separado. Se obtiene un slice de direcciones idénticas.
Ventajas:
Desventajas:
Iteran por índice y guardan un puntero al elemento requerido del array:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Ventajas:
Desventajas: