Genéricos en Go: Restricciones sin Plantillas
Go añadió parámetros de tipo en Go 1.18 para que puedas escribir funciones y tipos que funcionen en muchos tipos concretos, manteniendo la seguridad de tipos en tiempo de compilación.
Busca en todas las páginas de la documentación
Go añadió parámetros de tipo en Go 1.18 para que puedas escribir funciones y tipos que funcionen en muchos tipos concretos, manteniendo la seguridad de tipos en tiempo de compilación.
El diseño es deliberadamente más pequeño que los sistemas de plantillas en C++ o Java.
Las restricciones son interfaces ordinarias (con conjuntos de tipos), no un lenguaje de plantillas separado.
Esta página es el ancla conceptual de la sección de Genéricos.
Conceptos Básicos de Genéricos recopila fragmentos ejecutables; los artículos hermanos cubren la sintaxis de restricciones, comparable, estructuras de datos, ayudantes de la biblioteca estándar y compensaciones de rendimiento.
T) representa un tipo concreto en tiempo de compilación, y una interfaz de restricción limita qué tipos pueden reemplazar a T.interface{} (ahora any), el código repetitivo de go generate y los ayudantes copiados y pegados para slices, maps y contenedores sin perder el tipado estático.interface{} o generación de código.slices/maps/cmp de la biblioteca estándar, reflexión.Antes de Go 1.18, el código reutilizable a menudo usaba interface{} (hoy any), interfaces pequeñas o go generate para generar copias por tipo.
Cada enfoque sacrificaba seguridad o claridad por flexibilidad.
Genéricos añaden listas de tipos entre corchetes a funciones y tipos: func Max[T cmp.Ordered](a, b T) T.
El compilador verifica que cada tipo de argumento satisfaga la restricción antes de aceptar la llamada.
Una restricción es un tipo de interfaz.
Puede listar métodos (interface { String() string }), nombrar tipos en una unión (int | float64), o usar el operador de tipo subyacente ~int (cualquier tipo cuyo tipo subyacente sea int, incluyendo tipos enteros nombrados).
Las restricciones predefinidas any (todos los tipos) y comparable (tipos permitidos con == y !=) cubren los casos más comunes.
La instanciación es explícita o inferida: Max[int](1, 2) o Max(1, 2) cuando el compilador puede inferir T.
A diferencia de las plantillas de C++, Go no tiene una fase de expansión de macros ni ganchos de especialización definidos por el usuario.
El compilador monomorfiza: genera código máquina especializado (o IR) por cada instanciación distinta, similar en espíritu a generar MaxInt, MaxFloat64, etc., manualmente.
La verificación de tipos ocurre en dos fases.
Tiempo de definición: el cuerpo de la función genérica solo puede usar operaciones permitidas para cada tipo en el conjunto de tipos de la restricción.
Si T es solo int | float64, no puedes llamar a T.String() a menos que la restricción incluya ese método.
Tiempo de instanciación: cada sitio de llamada sustituye tipos concretos.
Si no existe una sustitución válida, la compilación falla con un error de restricción claro en lugar de un pánico en tiempo de ejecución por una mala aserción de tipo.
// Restricción: Tipos ordenados de cmp - int, float, string, etc.
func Min[T cmp.Ordered](a, b T) T {
if a < b {
return a
}
return b
}Los conjuntos de tipos son el conjunto de tipos que una restricción acepta.
~byte | ~rune acepta tanto byte como tipos nombrados personalizados con esos tipos subyacentes.
Los elementos de la unión deben ser comparables entre sí cuando la restricción se usa en contextos que lo requieren (por ejemplo, las claves de mapa necesitan comparable).
Los genéricos interactúan limpiamente con las interfaces: un parámetro de tipo restringido a una interfaz todavía usa la satisfacción implícita de interfaces de Go.
No introducen herencia nominal.
Los métodos no pueden declarar sus propios parámetros de tipo en Go; solo las funciones y los tipos nombrados a nivel de paquete llevan listas de parámetros de tipo.
Los métodos en tipos genéricos usan los parámetros del tipo (func (s *Stack[T]) Push(v T)).
definición genérica sitio de llamada
------------------ -----------------
func Map[F,T any](...) --> Map[string,int](...)
| |
v v
verificación de restricción monomorfizado
en F, T Map_string_int
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Genéricos | Tipado estático, sin conversiones | Ruido sintáctico, tamaño binario | Algoritmos de slice/map, contenedores |
| Interfaces pequeñas | APIs idiomáticas de Go | Despacho en tiempo de ejecución, código repetitivo | IO, plugins, contratos de comportamiento |
any + type switch | Análisis flexible | Errores en tiempo de ejecución, verboso | Adaptadores JSON, impresoras de depuración |
go generate | Costo cero en tiempo de ejecución, tipos explícitos | Complejidad del paso de compilación | Rutas críticas de alto rendimiento con muchos tipos |
| Reflexión | Una implementación | Lento, sin verificaciones de compilación | Plugins, esquemas desconocidos |
Bibliotecas como chi, gin y echo rara vez exponen firmas de manejador genéricas; los genéricos brillan en utilidades internas (ayudantes de paginación, cachés tipados) y utilidades compartidas de middleware de google.golang.org/grpc, no en cada firma de ruta pública.
Los proyectos controller-runtime y kubebuilder a menudo combinan la generación de código (para CRDs) con ayudantes de lista genéricos en paquetes compartidos.
tinygo y wazero tienen soporte variable según el objetivo; verifica el código con uso intensivo de genéricos en compilaciones integradas y WASM en CI.
golangci-lint puede marcar parámetros de tipo redundantes y APIs sobre-genéricas; combina genéricos con benchmarks cuando el tamaño del binario es importante (ver Implicaciones de Rendimiento).
La adopción gradual es idiomática: comienza con ayudantes no exportados, luego amplía las APIs una vez que las restricciones se estabilicen.
Prefiere la biblioteca estándar slices, maps y cmp antes de mantener copias personalizadas.
Cuando una restricción crece más allá de tres uniones, extrae un tipo de restricción nombrado (type Number interface { ~int \| ~float64 }) para mayor legibilidad.
any significa que puedo hacer cualquier cosa con T - Puedes almacenar cualquier valor, pero todavía no puedes usar == en T a menos que la restricción sea comparable, y no puedes llamar a métodos que no estén listados en la restricción.Go 1.18 añadió parámetros de tipo, interfaces de restricción con conjuntos de tipos e inferencia en los sitios de llamada.
No añadió métodos genéricos en tipos no genéricos, especialización de plantillas ni sobrecarga de operadores.
El conjunto de tipos es la colección de tipos concretos permitidos para reemplazar un parámetro de tipo.
Las interfaces de restricción definen ese conjunto a través de métodos, tipos subyacentes ~T y uniones |.
Las interfaces ya eran el mecanismo de composición de Go.
Reutilizarlas para restricciones mantiene una sola notación para "¿qué operaciones soporta T?" en lugar de inventar un segundo sistema de tipos.
El compilador emite código especializado por cada argumento de tipo distinto (por ejemplo, Stack[int] vs Stack[string]).
Cada versión se verifica de forma independiente, como duplicados escritos a mano.
Las claves de mapa, algunos primitivos de sincronización y cualquier algoritmo que use == en T necesitan comparable.
Los slices, maps y funciones no son comparables en Go.
Los métodos de un tipo genérico usan los parámetros del tipo.
No puedes añadir una nueva lista de parámetros de tipo a un método ordinario en un tipo no genérico.
La inferencia funciona cuando los tipos de los argumentos fijan cada parámetro.
Cuando falla, pasa argumentos de tipo explícitos: Convert[int](v).
Una restricción puede requerir métodos; los tipos concretos satisfacen implícitamente.
Un parámetro de tipo restringido a io.Reader acepta cualquier implementación de lector sin herencia.
Go 1.21 añadió los paquetes genéricos slices, maps y cmp.
Prefiérelos antes que las copias personalizadas.
Conceptos Básicos de Genéricos para ejemplos ejecutables, luego Parámetros de Tipo e Interfaces de Restricción para la sintaxis ~ y uniones.
El contenido conceptual se aplica a los genéricos de Go 1.18+.
Los ejemplos de la biblioteca estándar asumen Go 1.21+ para slices/maps/cmp.
Los diseñadores priorizaron la simplicidad, las compilaciones rápidas y los errores legibles sobre el poder de metaprogramación del nivel de C++.
Las restricciones mantienen las APIs genéricas fáciles de buscar y revisar.
~ y unionesVersiones de Stack: Esta página fue escrita para Go 1.26.x (predeterminado GC Green Tea, go fix modernizers - verificar parche en la compilación), chi (última versión - verificar en la compilación), gin (última versión - verificar en la compilación), echo (última versión - verificar en la compilación), google.golang.org/grpc (última versión - verificar en la compilación), sigs.k8s.io/controller-runtime (última versión - verificar en la compilación), kubebuilder (última versión - verificar en la compilación), tinygo (última versión - verificar objetivos de placa en la compilación), wazero (última versión - verificar en la compilación) y golangci-lint (última versión - verificar conjunto de linters en la compilación).
Revisado por Chris St. John·Última actualización: 19 jul 2026