Range sobre Slices y Arrays en Go: Guía de Iteración Técnica

La cláusula range es un iterador integrado en Go diseñado para recorrer estructuras de datos secuenciales, como slices y arrays, abstrayendo la gestión manual de índices y límites de memoria. Técnicamente, el compilador desglosa esta instrucción en un bucle for tradicional que recupera, en cada paso, tanto el índice entero como una copia del valor contenido en la posición actual de la memoria.

Este comportamiento existe en Go para proporcionar una sintaxis idiomática y segura que previene errores comunes de tipo off-by-one presentes en lenguajes de bajo nivel. A diferencia de C++, donde la iteración sobre contenedores puede implicar punteros complejos o iteradores que se invalidan fácilmente, Go garantiza una interfaz consistente. Al iterar, range devuelve dos valores: el primero representa el índice (tipo int) y el segundo es el elemento en sí. Esta dualidad resuelve el problema de legibilidad al eliminar la necesidad de acceder manualmente a la estructura mediante slice[i] dentro del cuerpo del bucle, optimizando tanto el tiempo de desarrollo como la claridad del código.

Mecánica de asignación y el identificador en blanco

Desde una perspectiva de bajo nivel, la expresión a la derecha de range se evalúa una sola vez antes de comenzar el ciclo. Si se trata de un slice, Go copia su header (una estructura que contiene el puntero al backing array, la longitud y la capacidad). Un punto crítico para el rendimiento y la lógica es que la variable de valor asignada en la iteración es una copia local. Esto significa que si el slice contiene estructuras grandes, Go realizará una copia de cada estructura en la variable del bucle en cada iteración.

Si la lógica del programa solo requiere acceso a los elementos y no al índice, se debe utilizar el identificador en blanco (_) para descartar el primer valor de retorno. Omitir este descarte y declarar una variable que no se utiliza resultará en un error de compilación. Por el contrario, si solo se requiere el índice, se puede omitir el segundo valor de retorno por completo.

package main

import "fmt"

func main() {
	numeros := []int{10, 20, 30}

	// 1. Uso estándar: índice y valor
	for i, v := range numeros {
		fmt.Printf("Índice: %d, Valor: %d\n", i, v)
	}

	// 2. Uso del identificador en blanco para omitir el índice
	for _, v := range numeros {
		// v es una COPIA. Modificar 'v' no altera 'numeros'
		v += 100 
	}
	fmt.Println("Slice tras intento de mod con v:", numeros) // → [10 20 30]

	// 3. Modificación correcta accediendo al slice original
	for i := range numeros {
		numeros[i] += 100
	}
	fmt.Println("Slice modificado por índice:", numeros) // → [110 120 130]
}
Go

La semántica de copia es el comportamiento más relevante: la variable v en for i, v := range reside en una dirección de memoria distinta a la del elemento dentro del slice. Modificar v solo altera esa variable temporal de ámbito local, dejando la colección original intacta.

Evaluación única del límite de iteración

Un comportamiento no obvio del runtime de Go es que el número de iteraciones se determina antes de entrar al bucle. Esto se debe a que range captura la longitud del slice al evaluar su expresión inicial.

Si se añaden elementos al slice mediante append dentro del cuerpo del bucle, el bucle no se ejecutará para esos nuevos elementos. La iteración se detendrá al alcanzar la longitud que tenía el slice en el momento exacto en que se inició el range. Sin embargo, si se modifican valores en índices existentes, esos cambios sí serán visibles en iteraciones posteriores del mismo bucle, ya que ambos (el slice original y la copia del header usada por el bucle) apuntan al mismo backing array subyacente.


  • Módulo: Control de Flujo
  • Artículo número: #56

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio