CAP 04 · LEC 03·Funciones

Closures y funciones anónimas

Una función anónima no tiene nombre; un closure es una función que recuerda las variables del scope donde se creó. Juntas son la base de patrones como callbacks, factories, generadores y middleware en Go.

● INTERMEDIO6 min lecturapor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

Funciones anónimas

Una función anónima es exactamente eso: una función sin nombre. Puedes definirla y ejecutarla en el sitio, o asignarla a una variable:

package main import "fmt" func main() { // Definir y ejecutar inmediatamente — IIFE func() { fmt.Println("Ejecutada al vuelo") }() // Con parámetros y argumentos result := func(a, b int) int { return a + b }(3, 4) fmt.Println(result) // 7 // Asignar a una variable y reusar square := func(n int) int { return n * n } fmt.Println(square(5)) // 25 fmt.Println(square(9)) // 81 }
SalidaEjecutada al vuelo 7 25 81

¿Qué es un closure?

Un closure es una función anónima (o cualquier función literal) que captura variables del scope que la rodea. Esas variables siguen vivas mientras el closure pueda usarlas:

package main import "fmt" // Devuelve un closure que "recuerda" prefix func makeGreeter(prefix string) func(string) string { return func(name string) string { return prefix + ", " + name + "!" } } func main() { hello := makeGreeter("Hola") goodMorning := makeGreeter("Buenos días") fmt.Println(hello("Ada")) // Hola, Ada! fmt.Println(hello("Carlos")) // Hola, Carlos! fmt.Println(goodMorning("Ada")) // Buenos días, Ada! }
SalidaHola, Ada! Hola, Carlos! Buenos días, Ada!
Captura por referencia

Go captura las variables del enclosing scope por referencia, no por valor. Si modificas la variable original, el closure verá el nuevo valor. Esto tiene implicaciones importantes (ver más abajo).

El contador clásico

El ejemplo canónico: un generador de IDs o contador con estado privado. La variable count es accesible solo desde el closure — un patrón de encapsulación sin necesidad de structs:

package main import "fmt" func counter() func() int { count := 0 return func() int { count++ return count } } func main() { next := counter() fmt.Println(next()) // 1 fmt.Println(next()) // 2 fmt.Println(next()) // 3 // Cada llamada a counter() crea un estado independiente other := counter() fmt.Println(other()) // 1 — empieza desde cero fmt.Println(next()) // 4 — el primer contador sigue avanzando }
Salida1 2 3 1 4

La trampa del loop variable

Antes de Go 1.22, las variables declaradas en el for se compartían entre todas las iteraciones. Capturarlas en un closure llevaba a un bug clásico:

package main import "fmt" func main() { funcs := []func() int{} // En Go 1.22+ cada iteración crea su propia 'i' // En Go <1.22 todas comparten la misma 'i' y al final vale 3 for i := 0; i < 3; i++ { funcs = append(funcs, func() int { return i }) } for _, f := range funcs { fmt.Println(f()) } // Go 1.22+: 0 1 2 // Go <1.22: 3 3 3 }
Salida0 1 2
Solución para Go <1.22

Si trabajas con una versión anterior, la solución idiomática era crear una copia local en cada iteración:

for i := 0; i < 3; i++ {
    i := i // shadow: nueva variable por iteración
    funcs = append(funcs, func() int { return i })
}

Desde Go 1.22 esto ya no es necesario — el compilador lo hace por ti.

Casos de uso reales

Los closures aparecen por todas partes en Go idiomático: callbacks, middlewares, defer, sync.Once, opciones funcionales y más.

package main import ( "fmt" "sort" ) func main() { // sort.Slice usa un closure como comparador people := []struct { Name string Age int }{ {"Ada", 36}, {"Bob", 29}, {"Carla", 41}, } sort.Slice(people, func(i, j int) bool { return people[i].Age < people[j].Age }) for _, p := range people { fmt.Println(p.Name, p.Age) } // Closure capturando un acumulador sum := 0 apply := func(n int) { sum += n } for _, n := range []int{1, 2, 3, 4, 5} { apply(n) } fmt.Println("Total:", sum) // Total: 15 }
SalidaBob 29 Ada 36 Carla 41 Total: 15