⏱️ Lectura: 12 min
Go lleva 17 años sin un tipo set nativo en su librería estándar: los desarrolladores todavía escriben map[T]bool o map[T]struct{} cada vez que necesitan uno. El 28 de julio de 2026 el Go Collections working group publicó la propuesta #80590, un issue paraguas que agrupa siete paquetes nuevos de colecciones genéricas candidateando para Go 1.28: sets, mapas ordenados y un heap rediseñado.
📑 En este artículo
- TL;DR
- Introducción
- Qué pasó
- Contexto e historia
- Detalles técnicos y rendimiento de las colecciones genéricas
- Cómo empezar / probarlo
- Impacto y análisis
- Qué sigue
- Preguntas frecuentes
- ¿Qué es exactamente la propuesta #80590?
- ¿Cuándo va a estar disponible en una versión estable de Go?
- ¿Por qué Go no tuvo sets nativos hasta ahora?
- ¿Qué diferencia hay entre container/set.Set y container/mapset?
- ¿Puedo usar alguno de estos paquetes en producción hoy?
- ¿container/heap/v2 reemplaza al container/heap actual?
- Referencias
TL;DR
- El Go Collections working group publicó la propuesta #80590 el 28 de julio de 2026 para Go 1.28.
- El grupo lo integran Jonathan Amsterdam, Alan Donovan, Robert Griesemer, Daniel Martí, Roger Peppe, Keith Randall e Ian Lance Taylor.
- hash/maphash.Hasher (issue #70471) ya se liberó en Go 1.27 y define hash y equivalencia para tipos no comparables.
- container/set.Set[T] (issue #69230) se representa como map[T]struct{} y suma Union, Intersection y más operaciones de set.
- container/ordered.Map[K,V] (issue #60630) usa un árbol balanceado para permitir range queries ordenadas.
- container/heap/v2.Heap (issue #77397) reemplaza la API actual de container/heap, que el equipo describe como difícil de usar.
- La propuesta también evalúa mapas hash con orden de inserción (issue #80194) y stacks para una futura ronda.
- Los nuevos paquetes viven dentro del árbol container ya existente para no chocar con el concepto de contenedores de Linux.
Introducción
El lenguaje sumó generics en Go 1.18 (2022) e iteradores nativos en Go 1.23 (2024). Esas dos piezas dejaron la puerta abierta para que tipos definidos en paquetes de librería alcancen una ergonomía comparable a la de slice y map, los únicos tipos de colección que Go trae de fábrica. La propuesta #80590 es el resultado de ese trabajo: un conjunto de colecciones genéricas pensadas para cubrir los huecos más citados por la comunidad Go en los últimos años, sin comprometer la simplicidad que caracteriza al lenguaje.
Qué pasó
El working group lo integran Jonathan Amsterdam, Alan Donovan, Robert Griesemer, Daniel Martí, Roger Peppe, Keith Randall e Ian Lance Taylor, todos con historial en el equipo central de Go. Se formó a fines de 2025 con un objetivo puntual: llevar estructuras de datos comunes a la librería estándar siguiendo los mismos principios de pragmatismo y simplicidad que rigen el resto del lenguaje.
La propuesta no es un solo paquete sino una sombrilla que enlaza siete piezas, cada una con su propio issue y su propio CL (change list, el equivalente en Gerrit a un pull request) en revisión activa. Tres ya tienen implementación publicada, dos están en discusión de diseño y dos quedaron marcadas para una futura ronda, entre ellas mapas hash con orden de inserción (issue #80194) y stacks genéricos.
Contexto e historia
Go nunca ocultó su apuesta por la simplicidad de slice y map como tipos incorporados, pero esa misma apuesta dejó afuera estructuras que otros lenguajes dan por sentadas. No hay sets nativos: la convención histórica es map[T]bool o map[T]struct{}, con la ambigüedad de que un valor false en un map[T]bool puede confundirse con la ausencia de la clave. Tampoco hay mapas ni sets ordenados basados en árboles: quedaban completamente afuera de la librería estándar.
El patrón habitual en Go para ordenar datos, construir un map[K]V y después ordenar sus claves con sort.Slice, funciona bien en la mayoría de los casos. Pero falla cuando el programa necesita una range query, por ejemplo todos los usuarios con ID entre 1000 y 2000, sin recorrer el mapa entero. Ahí es donde container/ordered.Map, respaldado por un árbol balanceado, cambia el enfoque: una consulta por rango deja de requerir un recorrido completo de la estructura.
📌 Nota: los métodos de set como Union(S) S no forman una interfaz común entre distintos tipos concretos de set: es el clásico problema del método binario. El working group lo resuelve con interfaces de restricción recursivas (F-bounded polymorphism) que permiten escribir funciones genéricas como Subset o ContainsAny sin fijar un tipo concreto de antemano.
Detalles técnicos y rendimiento de las colecciones genéricas
Cada paquete de la propuesta ataca un caso de uso puntual, y juntos empiezan a delinear una convención para las colecciones genéricas dentro del árbol container de la librería estándar. El equipo aclaró que prefiere el término collection en vez de container para no confundirlo con los contenedores al estilo Linux, aunque el código siga viviendo en ese árbol por compatibilidad histórica.
| Paquete | Qué resuelve | Estado (julio 2026) | Referencia |
|---|---|---|---|
| hash/maphash.Hasher | Hash y equivalencia personalizados para claves no comparables | Liberado en Go 1.27 | issue #70471 |
| container/hash.Map[K,V] | Mapa hash que acepta el Hasher personalizado | En CL, propuesto para 1.28 | issue #69559 |
| container/hash.Set[T] | Set basado en un hash personalizado | En CL, propuesto para 1.28 | issue #80584 |
| container/set.Set[T] | Set canónico para tipos comparables, con Union e Intersection | En CL, propuesto para 1.28 | issue #69230 |
| container/mapset | Funciones helper para tratar map[T]bool como set sin cambiar su tipo | En CL, propuesto para 1.28 | issue #77052 |
| container/ordered.Map[K,V] | Mapa ordenado con árbol balanceado, útil para range queries | Propuesto para 1.28 | issue #60630 |
| container/heap/v2.Heap | Heap genérico que reemplaza la API actual, considerada difícil de usar | Propuesto para 1.28 | issue #77397 |
hash/maphash.Hasher, la pieza que ya llegó a Go 1.27, es la base de container/hash.Map y container/hash.Set: define una interfaz estándar de hash y equivalencia para cuando el tipo de clave no es comparable, como un slice o un map, o cuando la comparación por defecto del compilador da un resultado incorrecto, como pasa con los valores types.Type del paquete go/types, que necesitan comparación profunda (types.Identical) en vez de comparación de puntero. La documentación del paquete incluye un ejemplo de uso en un filtro de Bloom.
package main
import (
"fmt"
"container/set"
)
func main() {
frameworks := set.Of("gin", "echo", "fiber")
frameworks.Add("chi")
fmt.Println(frameworks.Contains("echo")) // true
fmt.Println(frameworks.Len()) // 4
}
Este primer ejemplo crea un set de nombres de frameworks y agrega un elemento con Add. A diferencia de map[T]bool, no hay forma de confundir un valor false con una clave ausente: Contains devuelve directamente un booleano de pertenencia.
package main
import (
"fmt"
"container/set"
)
func main() {
backendDevs := set.Of("ana", "bruno", "carla", "diego")
frontendDevs := set.Of("carla", "diego", "elena", "fabio")
fullstack := backendDevs.Intersection(frontendDevs)
equipoCompleto := backendDevs.Union(frontendDevs)
fmt.Println("Fullstack:", fullstack.Values())
fmt.Println("Equipo completo:", equipoCompleto.Len()) // 6
}
Este segundo ejemplo es más cercano a un caso real: cruzar dos equipos con Intersection para encontrar a los desarrolladores fullstack, y con Union para armar la nómina completa sin duplicados. Son las mismas operaciones que hoy cualquier equipo Go reimplementa a mano sobre un map[T]struct{}.
flowchart TD
A["Propuesta en GitHub Issue"] --> B["CL en Gerrit (Code Review)"]
B --> C["Revision del proposal committee"]
C --> D{"Aceptada?"}
D -->|"Si"| E["Merge en golang/go"]
D -->|"No"| F["Cerrada o iterada"]
E --> G[("go1.28 stdlib")]
⚠️ Ojo: ninguno de estos paquetes, salvo hash/maphash.Hasher, forma parte todavía de un release estable de Go. Las firmas de método y los nombres de paquete pueden cambiar durante la revisión de la propuesta antes de llegar a Go 1.28.
Cómo empezar / probarlo
hash/maphash.Hasher ya se puede usar hoy porque viajó en Go 1.27: alcanza con import "hash/maphash" en cualquier proyecto con go.mod apuntando a esa versión o superior. El resto de los paquetes todavía vive en CLs individuales dentro del sistema Gerrit de revisión de Go, así que para probarlos antes del release hay que compilar el toolchain desde la rama de desarrollo (gotip) y aplicar el CL correspondiente, el mismo flujo que usa cualquier contribuidor del proyecto.
# Los mismos comandos funcionan igual en Linux, macOS y Windows (PowerShell o cmd)
go install golang.org/dl/gotip@latest
gotip download
gotip version
Con gotip instalado y el CL de container/set.Set aplicado sobre el checkout local, se puede confirmar que la API está disponible inspeccionando la documentación generada directamente desde el código fuente:
gotip doc container/set.Set
Ese comando lista los métodos exportados del tipo en ese punto del CL (Add, Contains, Union, Intersection y demás), la forma más directa de verificar qué firma quedó vigente en cada revisión sin depender de capturas de pantalla desactualizadas.
Impacto y análisis
Para el desarrollador de a pie, el cambio más inmediato no es una API nueva sino una convención: hoy, dos proyectos Go que necesitan un set eligen entre map[T]bool, map[T]struct{} o una librería de terceros, cada una con su propia superficie de API. Con container/set.Set como set canónico en la librería estándar, ese fragmento repetido en cada repositorio deja de tener sentido, y las firmas de funciones públicas de otras librerías pueden empezar a aceptar set.Set[T] como tipo de parámetro sin forzar una dependencia externa.
El caso de container/mapset apunta al lado opuesto del problema: código existente que ya usa map[T]bool o map[T]struct{} como set y no puede cambiar su tipo público sin romper compatibilidad. container/mapset ofrece funciones sueltas (Union, Intersection y otras) que operan sobre esos mapas legacy con exactamente la misma semántica que los métodos de set.Set, sin migrar el tipo subyacente.
El working group fue explícito sobre el alcance inicial: las primeras implementaciones buscan cumplir la API y el comportamiento asintótico esperado de la forma más simple posible. La optimización de constantes, reducir allocations o mejorar el uso de caché de CPU, queda fuera del proceso de propuesta y llegará, si llega, en iteraciones posteriores una vez que el diseño esté fijado.
Qué sigue
La propuesta está abierta a discusión pública en el issue #80590, con siete sub-propuestas enlazadas que se revisan y aceptan, o rechazan, de forma independiente dentro del proceso estándar de Go. El working group ya adelantó que evalúa una octava pieza, mapas hash con orden de inserción (issue #80194), y un tipo stack genérico para una ronda posterior, una vez resueltas las siete piezas actuales.
El milestone asignado es Go1.28, lo que en el calendario habitual de releases semestrales de Go ubicaría el lanzamiento estable hacia comienzos de 2027, aunque el proceso de propuesta puede extender o recortar ese calendario según el resultado de la revisión pública.
📖 Resumen en Telegram: Ver resumen
Probalo vos: corré go install golang.org/dl/gotip@latest && gotip download hoy mismo y aplicá el CL de container/set.Set del issue #69230 para ver el set canónico de Go antes de que llegue a un release estable.
Preguntas frecuentes
¿Qué es exactamente la propuesta #80590?
Es un issue paraguas del Go Collections working group que agrupa siete propuestas de paquetes de colecciones genéricas (sets, mapas hash, mapas ordenados y un heap rediseñado) candidateando para la librería estándar de Go 1.28.
¿Cuándo va a estar disponible en una versión estable de Go?
El milestone asignado es Go1.28. Solo hash/maphash.Hasher ya se liberó, en Go 1.27; el resto sigue en revisión de CL y puede cambiar antes del release final.
¿Por qué Go no tuvo sets nativos hasta ahora?
El lenguaje priorizó desde su diseño original la flexibilidad de slice y map como tipos incorporados. Recién con generics (Go 1.18) e iteradores (Go 1.23) se volvió práctico dar a tipos definidos en paquetes de librería una ergonomía comparable a la de los tipos nativos.
¿Qué diferencia hay entre container/set.Set y container/mapset?
container/set.Set[T] es un tipo nuevo, representado internamente como map[T]struct{}, con métodos como Union e Intersection. container/mapset ofrece las mismas operaciones como funciones sueltas para código legacy que ya usa map[T]bool o map[T]struct{} y no puede cambiar de tipo público.
¿Puedo usar alguno de estos paquetes en producción hoy?
Solo hash/maphash.Hasher, que viajó estable en Go 1.27. El resto vive en CLs individuales sobre gotip y todavía puede cambiar de nombre o de firma antes de llegar a un release estable.
¿container/heap/v2 reemplaza al container/heap actual?
Sí, es una API genérica pensada para reemplazar la interfaz heap.Interface actual, que el propio equipo de Go describe como difícil de usar correctamente.
Referencias
- golang/go issue #80590: la propuesta paraguas del Go Collections working group, fuente primaria de este artículo.
- pkg.go.dev/hash/maphash: documentación oficial del paquete ya liberado en Go 1.27, incluye Hasher y el ejemplo de filtro de Bloom.
- go.dev/doc: documentación oficial del lenguaje y guía de contribución para compilar gotip y aplicar CLs.
- github.com/golang/go: repositorio principal del proyecto, donde se discuten y revisan todos los CLs mencionados.
📱 ¿Te gusta este contenido? Únete a nuestro canal de Telegram @programacion donde publicamos a diario lo más relevante de tecnología, IA y desarrollo. Resúmenes rápidos, contenido fresco todos los días.
Imagen destacada: Foto de Pankaj Patel en Unsplash
0 Comentarios