ProgrammazioneMiddle/Senior Go backend developer

Как funcionan los ciclos for-range sobre matrices y maps en Go y qué particularidades surgen al copiar valores al utilizarlos?

Supera i colloqui con l'assistente IA Hintsage

Respuesta.

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:

  • En cada iteración del ciclo se crean nuevas copias de las variables key/value
  • Tomar la dirección de value dentro de for-range devuelve no la dirección del elemento en la colección, sino la dirección de una variable temporal
  • Para el map, la iteración ocurre en un orden arbitrario, sin garantía de secuencia

Preguntas con trampa.

¿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.

Errores típicos y anti-patrones

  • Uso de &value en range con la intención de guardar referencias a diferentes elementos
  • Modificación de la variable value en lugar de acceder por índice
  • Esperar un orden secuencial de recorrido en map

Ejemplo de la vida real

Caso negativo

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:

  • Concisión del ciclo

Desventajas:

  • Todas las referencias son iguales, al cambiar un elemento cambian "todos"

Caso positivo

Iteran por índice y guardan un puntero al elemento deseado del arreglo:

for i := range arr { ptrs = append(ptrs, &arr[i]) }

Ventajas:

  • Cada puntero apunta a un elemento separado del slice original

Desventajas:

  • Sintaxis más larga, pero resultado predecible