Iteradores, range-over-func y herramientas modernas

Avanzado
Go
Go
Actualizado: 03/04/2026

Variables de bucle seguras

Go garantiza que cada iteración de un bucle for crea una nueva variable de iteración. Esto elimina un error clásico al capturar variables en goroutines o closures:

Iteradores en Go: range-over-func, iter.Seq y biblioteca estándar

nombres := []string{"Ana", "Luis", "Marta"}

for _, nombre := range nombres {
    go func() {
        fmt.Println(nombre) // cada goroutine captura su propia copia
    }()
}

Cada goroutine captura su propia variable nombre, por lo que se imprimen los tres nombres correctamente (en cualquier orden). Este comportamiento es especialmente importante al lanzar goroutines o pasar funciones como callbacks dentro de bucles.

Las variables de iteración son independientes en cada ciclo del bucle. No es necesario crear copias manuales como n := nombre dentro del bucle.

Range sobre enteros

Go permite iterar directamente sobre un rango de enteros con for range, lo que simplifica los bucles de conteo:

// Iterar de 0 a 4
for i := range 5 {
    fmt.Print(i, " ") // 0 1 2 3 4
}

// Sin variable de iteración
for range 10 {
    fmt.Println("Repetición")
}

// Útil para inicializar colecciones
workers := make([]Worker, 0, 10)
for range 10 {
    workers = append(workers, NuevoWorker())
}

Esta sintaxis es más concisa y menos propensa a errores que el bucle clásico for i := 0; i < n; i++.

Iteradores con range-over-func

Go soporta el uso de for range sobre funciones iteradoras que siguen las firmas del paquete iter. Esto permite crear generadores y secuencias perezosas que se consumen con la misma sintaxis que slices y mapas.

Tipos de iteradores

import "iter"

// iter.Seq[V]     — secuencia de un valor por iteración
// iter.Seq2[K, V] — secuencia de dos valores por iteración

Crear un iterador personalizado

// Generador de números de Fibonacci
func fibonacci() iter.Seq[int] {
    return func(yield func(int) bool) {
        a, b := 0, 1
        for {
            if !yield(a) {
                return // el consumidor llamó a break
            }
            a, b = b, a+b
        }
    }
}

// Consumir con for range
for n := range fibonacci() {
    if n > 1000 {
        break
    }
    fmt.Print(n, " ")
}
// 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987

La función yield produce un valor y devuelve false si el consumidor ha dejado de iterar (por ejemplo, con break). Este patrón permite crear secuencias infinitas de forma segura.

Iteradores en la biblioteca estándar

Los paquetes slices y maps exponen iteradores que se combinan con for range:

import (
    "maps"
    "slices"
)

numeros := []int{10, 20, 30, 40, 50}

// slices.All: devuelve índice y valor
for i, v := range slices.All(numeros) {
    fmt.Printf("[%d]=%d ", i, v)
}

// slices.Values: devuelve solo valores
for v := range slices.Values(numeros) {
    fmt.Print(v, " ")
}

// maps.Keys y maps.Values
catalogo := map[string]float64{"teclado": 59.99, "ratón": 25.50}
for clave := range maps.Keys(catalogo) {
    fmt.Println(clave)
}

Iteradores Seq2 personalizados

func elementosConIndice[T any](items []T) iter.Seq2[int, T] {
    return func(yield func(int, T) bool) {
        for i, item := range items {
            if !yield(i, item) {
                return
            }
        }
    }
}

palabras := []string{"hola", "mundo", "go"}
for i, p := range elementosConIndice(palabras) {
    fmt.Printf("%d: %s\n", i, p)
}

Los iteradores son una abstracción que unifica el acceso a secuencias, ya sean finitas o infinitas. Utiliza iter.Seq cuando necesites producir valores bajo demanda sin materializar toda la colección en memoria.

Números aleatorios: math/rand/v2

El paquete math/rand/v2 ofrece una API simplificada con algoritmos modernos (ChaCha8, PCG) que no requiere inicialización manual de semilla:

import "math/rand/v2"

// API simplificada
n := rand.IntN(100)      // entero en [0, 100)
f := rand.Float64()      // flotante en [0.0, 1.0)
b := rand.Int64()        // int64 aleatorio

// Permutar un slice
elementos := []string{"a", "b", "c", "d", "e"}
rand.Shuffle(len(elementos), func(i, j int) {
    elementos[i], elementos[j] = elementos[j], elementos[i]
})

// Generador con fuente determinista (útil para pruebas)
fuente := rand.NewPCG(42, 0)
rng := rand.New(fuente)
fmt.Println(rng.IntN(100)) // siempre el mismo resultado para la semilla 42

Herramientas de desarrollo con go tool

Go permite declarar y gestionar herramientas de desarrollo directamente en go.mod, garantizando que todos los desarrolladores del proyecto usen las mismas versiones:

# Añadir herramientas al módulo
go get -tool golang.org/x/tools/cmd/stringer@latest
go get -tool github.com/golangci/golangci-lint/cmd/golangci-lint@latest

El go.mod incluye una sección tool:

tool (
    golang.org/x/tools/cmd/stringer
    github.com/golangci/golangci-lint/cmd/golangci-lint
)

Ejecutar una herramienta:

go tool stringer -type=Direction ./...
go tool golangci-lint run

Las herramientas declaradas en go.mod se versionan junto con el código del proyecto. Esto elimina la necesidad de scripts de instalación separados y garantiza reproducibilidad en el equipo y en CI/CD.

Punteros débiles: el paquete weak

El paquete weak permite crear punteros débiles que no impiden la recolección de basura. Son útiles para implementar caches en aplicaciones de larga ejecución:

import "weak"

type Cache struct {
    datos map[string]weak.Pointer[[]byte]
}

func (c *Cache) Obtener(clave string) ([]byte, bool) {
    if ptr, ok := c.datos[clave]; ok {
        if val := ptr.Value(); val != nil {
            return *val, true
        }
        // El GC recolectó el valor: eliminamos la entrada muerta
        delete(c.datos, clave)
    }
    return nil, false
}

Los punteros débiles mantienen una referencia que no cuenta para el recolector de basura. Si no hay otras referencias fuertes al objeto, el GC puede recolectarlo y el puntero débil devolverá nil.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Go

Explora más contenido relacionado con Go y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Comprender la semántica de variables de bucle por iteración. Usar for range sobre enteros. Crear y consumir iteradores con iter.Seq e iter.Seq2. Generar números aleatorios con math/rand/v2. Gestionar herramientas de desarrollo con go tool.