Go 1.22: variables de bucle y ServeMux, lo que sigue vigente en 2026
Índice de contenidos
- Puntos clave
- ¿Qué cambió en las variables de bucle?
- ¿Cómo se quitan las copias v := v?
- ¿Qué patrones admite ServeMux?
- ¿Qué ha cambiado en ServeMux desde Go 1.22?
- ¿Qué más trajo Go 1.22 y en qué quedó?
- ¿Cómo se actualiza hoy un módulo anterior a Go 1.22?
- Preguntas frecuentes
- ¿El comportamiento nuevo de los bucles se activa solo con instalar un Go reciente?
- ¿Puedo dejar gorilla/mux o chi y usar solo ServeMux?
- ¿Sigue teniendo soporte Go 1.22?
- Conclusión
- Fuentes
Probado con Go 1.27.3 (golang:1.27.3, arm64) · verificado
Actualizado: 2026-10-27
Go 1.22, de febrero de 2024, trajo dos cambios que siguen definiendo el Go idiomático en Go 1.27: cada iteración de un for tiene su propia variable si go.mod declara go 1.22 o superior, y net/http.ServeMux enruta por método y comodines como GET /users/{id}. Esta guía los comprueba con Go 1.27.3 y anota qué cambió después.
Go 1.22, publicado el 6 de febrero de 2024, trajo dos cambios que siguen definiendo el Go idiomático en 2026: cada iteración de un for tiene su propia variable y net/http.ServeMux enruta por método y con comodines. La versión 1.22 ya no tiene soporte, porque Go solo mantiene las dos últimas versiones mayores, que el 27 de octubre de 2026 son la 1.27 y la 1.26. Esos dos cambios, en cambio, se mantienen y las versiones posteriores han construido sobre ellos. Esta guía los comprueba con Go 1.27.3 y anota qué cambió desde entonces. Está disponible en inglés.
Puntos clave
- Desde Go 1.22, la variable de un
forse crea de nuevo en cada iteración, pero solo en los módulos cuyogo.moddeclarago 1.22o superior. - Con Go 1.27.3, el mismo bucle imprime
c c ccongo 1.21engo.mody las tres letras distintas congo 1.22. go fix, rehecho en Go 1.26, elimina las copiasv := vque la semántica nueva ha vuelto innecesarias.- Los patrones de
ServeMuxaceptan método, comodines con nombre como{id}y comodines de resto como{path...}, y responden 405 con la cabeceraAllowcuando el método no encaja. - Go 1.23 añadió
Request.Pattern, Go 1.26 cambió las redirecciones de barra final de 301 a 307, yGODEBUG=httpmuxgo121=1sigue restaurando el enrutador antiguo en Go 1.27.3.
¿Qué cambió en las variables de bucle?
Hasta Go 1.21, la variable declarada en la cabecera de un for era una sola para todo el bucle. Cada closure o goroutine creada dentro capturaba esa misma variable, no su valor en esa vuelta, y al ejecutarse más tarde veía el último valor. El equipo de Go describe el problema en Fixing For Loops in Go 1.22[1], donde cita un incidente público de Let’s Encrypt[2] causado por esta captura.
Este programa lanza una goroutine por elemento y cada una imprime el suyo:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for _, v := range []string{"a", "b", "c"} {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Print(v, " ")
}()
}
wg.Wait()
fmt.Println()
}
Lo ejecutamos el 27 de octubre de 2026 con la imagen golang:1.27.3, tres veces con cada directiva go en el go.mod. Con go 1.21, las tres ejecuciones imprimieron c c c. Con go 1.22, las tres imprimieron c a b: cada goroutine vio su propia letra, en el orden en que el planificador las ejecutó.
El resultado muestra la regla que hace segura la transición. La semántica nueva depende de la directiva go del módulo, no de la versión del compilador, así que un toolchain actual sigue compilando con la semántica antigua el código que declara una versión anterior. Antes de publicarla, Google forzó la semántica nueva en todas sus compilaciones internas desde mayo de 2023. Según el mismo artículo, en cuatro meses no hubo ningún problema en código de producción, aunque sí tuvieron que corregir pruebas que no comprobaban lo que creían.
¿Cómo se quitan las copias v := v?
Con la semántica nueva, la copia defensiva v := v dentro del bucle sobra. Go 1.26 rehízo go fix como la herramienta de los modernizadores, según sus notas de la versión[3], y uno de ellos elimina esas copias en los módulos que ya declaran go 1.22 o superior. Sobre un bucle con la copia, go fix -diff ./... en Go 1.27.3 propuso este cambio:
for _, v := range []int{1, 2, 3} {
- v := v
fns = append(fns, func() { fmt.Print(v) })
}
Sin -diff, go fix ./... aplica el cambio en los ficheros. Revísalo antes de confirmarlo si alguna copia era intencionada.
¿Qué patrones admite ServeMux?
El segundo cambio de Go 1.22 está en net/http.ServeMux. Hasta la 1.21, el multiplexor estándar solo comparaba prefijos de ruta, sin métodos ni parámetros, y cualquier API REST tiraba de gorilla/mux o chi. Desde la 1.22, un patrón puede llevar el método delante, segmentos con nombre entre llaves y un comodín final que recoge el resto de la ruta. Lo explica el artículo Routing Enhancements for Go 1.22[4] y lo detalla la documentación de ServeMux[5].
Este programa registra dos rutas y lanza tres peticiones contra el multiplexor con httptest, sin abrir ningún puerto:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "user %s (%s)", r.PathValue("id"), r.Pattern)
})
mux.HandleFunc("GET /files/{path...}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "file %s", r.PathValue("path"))
})
for _, req := range []struct{ m, p string }{
{"GET", "/users/42"}, {"POST", "/users/42"}, {"GET", "/files/a/b.txt"},
} {
rec := httptest.NewRecorder()
mux.ServeHTTP(rec, httptest.NewRequest(req.m, req.p, nil))
fmt.Println(req.m, req.p, rec.Code, rec.Header().Get("Allow"), rec.Body.String())
}
}
Con Go 1.27.3, la salida fue esta:
GET /users/42 200 user 42 (GET /users/{id})
POST /users/42 405 GET, HEAD Method Not Allowed
GET /files/a/b.txt 200 file a/b.txt
La segunda línea es la que más trabajo ahorra. El multiplexor sabe que la ruta existe para otro método, responde 405 y rellena la cabecera Allow con GET, HEAD, porque un patrón GET también atiende HEAD. El comodín {path...} recoge a/b.txt con su barra incluida, y r.PathValue lee los valores sin parsear la URL a mano.
¿Qué ha cambiado en ServeMux desde Go 1.22?
Las versiones posteriores han pulido el enrutador sin cambiar su sintaxis. Estos son los cambios que afectan a código que ya lo usa:
| Versión | Fecha | Cambio en net/http |
|---|---|---|
| Go 1.23 | 13-08-2024 | Request.Pattern con el patrón que encajó |
| Go 1.23 | 13-08-2024 | Espacios o tabuladores tras el método |
| Go 1.25 | 12-08-2025 | CrossOriginProtection contra CSRF |
| Go 1.26 | 10-02-2026 | Redirección de barra final con 307 |
El cambio de Go 1.26 es el único que altera respuestas existentes. Según las notas de Go 1.26[3], una petición a /docs contra un patrón /docs/ recibe ahora un 307 en lugar de un 301, y así respondió Go 1.27.3 en nuestra prueba. Request.Pattern es útil para métricas y trazas, porque etiqueta cada petición con su ruta y no con la URL concreta. Y si una migración necesita el comportamiento anterior a la 1.22, GODEBUG=httpmuxgo121=1 lo restaura: con esa variable, el mismo programa respondió 404 a las tres peticiones en Go 1.27.3.
Para un servicio REST pequeño o mediano, el multiplexor estándar basta. Un enrutador externo sigue teniendo sentido cuando necesitas grupos de rutas con middleware compartido, validación o generación de OpenAPI.
¿Qué más trajo Go 1.22 y en qué quedó?
El resto de novedades de Go 1.22 también sigue en pie, y dos de ellas abrieron camino a cambios mayores:
for i := range 10: el rango sobre enteros llegó en la 1.22, y la 1.23 lo amplió al rango sobre funciones iteradoras, la base de los iteradores deslices,mapsy, en la 1.26, dereflect.math/rand/v2: fue el primer paquete/v2de la biblioteca estándar, con el generador ChaCha8 y funciones comoIntN.- Directiva
toolchain: llegó en la 1.21 y permite quegodescargue el compilador que pide el módulo; con ella conviven sin problemas la 1.26 y la 1.27 en un mismo equipo.
Si trabajas con genéricos, la guía sobre generics en Go tres años después cuenta qué ha quedado de ellos en el código real.
¿Cómo se actualiza hoy un módulo anterior a Go 1.22?
Un módulo que todavía declara go 1.21 o anterior compila con Go 1.27, pero conserva la semántica antigua de los bucles. Para pasarlo a la nueva, sigue estos pasos en orden:
- Sube la directiva con
go mod edit -go=1.26, la versión soportada más antigua, para no exigir la 1.27 a quien compile el módulo. - Ejecuta
go vet ./...ygo test ./...: una prueba que dependiera de la variable compartida fallará aquí. - Ejecuta
go fix -diff ./..., revisa los cambios y aplícalos congo fix ./....
Preguntas frecuentes
¿El comportamiento nuevo de los bucles se activa solo con instalar un Go reciente?
No. La variable por iteración depende de la directiva go del go.mod, no del compilador. Con Go 1.27.3 y go 1.21 en el go.mod, nuestro programa de prueba siguió imprimiendo c c c; al cambiar la directiva a go 1.22, cada goroutine imprimió su propia letra.
¿Puedo dejar gorilla/mux o chi y usar solo ServeMux?
Para un servicio REST pequeño o mediano, sí. Desde Go 1.22, ServeMux acepta el método en el patrón, comodines con nombre que se leen con r.PathValue y comodines de resto con {name...}, y responde 405 con la cabecera Allow cuando el método no encaja. Un enrutador externo aporta valor con grupos de rutas, middleware por grupo, validación o generación de OpenAPI.
¿Sigue teniendo soporte Go 1.22?
No. Go mantiene cada versión mayor hasta que salen dos más nuevas, así que la 1.22 perdió el soporte con la llegada de la 1.24, el 11 de febrero de 2025. El 27 de octubre de 2026, las versiones con soporte son la 1.27 (última, 1.27.3) y la 1.26 (última, 1.26.10).
Conclusión
De todo lo que trajo Go 1.22, dos cambios siguen marcando cómo se escribe Go en 2026. La variable de bucle por iteración eliminó una clase entera de fallos con goroutines, y el ServeMux con métodos y comodines dejó el enrutador externo para los casos que lo necesitan. Las versiones posteriores los han pulido sin romperlos, y go fix limpia hoy los restos de la semántica antigua. Si mantienes un módulo que aún declara go 1.21, actualizar la directiva es la parte que más rinde de toda la migración.
Fuentes: [1] Go 1.22 Release Notes[6], [2] Fixing For Loops in Go 1.22, The Go Blog[1], [3] Routing Enhancements for Go 1.22, The Go Blog[4], [4] Go 1.26 Release Notes[3], [5] Go release history[7].