Generics en Go: para qué sirven y cómo usarlos para principiantes

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿qué son los Generics y por qué aparecieron en Go?



Durante mucho tiempo, la comunidad de Go (Golang) prescindió de los genéricos (programación genérica). Los desarrolladores usaban interfaces, interface{} vacíos y generación de código para sortear las limitaciones del tipado estricto. En la versión Go 1.18 (febrero de 2022), finalmente se agregaron al lenguaje los genéricos, una herramienta poderosa que permite escribir funciones y estructuras que trabajan con diferentes tipos de datos sin perder la seguridad de tipos.



En este artículo, analizaremos para qué sirven los Generics en Go, cómo funcionan y aprenderemos a aplicarlos en la práctica. El material está dirigido a desarrolladores principiantes que ya conocen los fundamentos de Go: variables, funciones y slices.



¿Para qué sirven los genéricos en Go?



Imagina que necesitas escribir una función que encuentre el valor mínimo en un slice de números. Sin genéricos, tendrías que escribir funciones separadas para int, float64 y otros tipos:



// Sin genéricos — tres funciones idénticasfunc MinInt(a, b int) int {    if a < b {        return a    }    return b}

func MinFloat64(a, b float64) float64 { if a < b { return a } return b}


Esto provoca duplicación de código. Antes, este problema se resolvía mediante interface{} y conversión de tipos, pero este enfoque perdía seguridad en la etapa de compilación:



func Min(a, b interface{}) interface{} {    // Comparación y conversión — fácil equivocarse    return a}


Los genéricos resuelven tres problemas clave:

  • Seguridad de tipos — los errores de tipo se detectan en tiempo de compilación.
  • Eliminación de duplicación — una función funciona con cualquier tipo.
  • Legibilidad del código — indicación explícita de qué tipos son compatibles.


Fundamentos de sintaxis: type parameters y constraints



Los genéricos en Go se basan en dos conceptos: parámetros de tipo (type parameters) y restricciones (constraints).



Parámetros de tipo



Los parámetros de tipo se indican entre corchetes después del nombre de la función o tipo. Por convención, se suele usar la letra T (de Type), pero se puede usar cualquier nombre:



// Función con un parámetro de tipo Tfunc Print[T any](value T) {    fmt.Println(value)}

// UsoPrint[int](42) // indicamos el tipo explícitamentePrint("Hello") // el compilador deduce el tipo automáticamente


La palabra clave any es un alias para la interfaz vacía interface{}. Significa que la función acepta cualquier tipo.



Restricciones (constraints)



Las restricciones definen qué tipos pueden usarse con un genérico. Por ejemplo, para comparar números, el tipo debe soportar los operadores < y >. Las restricciones integradas para tipos numéricos se encuentran en el paquete golang.org/x/exp/constraints:



import "golang.org/x/exp/constraints"

// Restricción — solo números enterosfunc Sum[T constraints.Integer](numbers []T) T { var total T for _, n := range numbers { total += n } return total}


También puedes crear tus propias restricciones mediante interfaces:



// Restricción propia: el tipo debe tener un método String()type Stringer interface {    String() string}

func Describe[T Stringer](value T) string { return "Value: " + value.String()}


Ejemplos prácticos de uso de Generics



Veamos varios escenarios reales donde los genéricos simplifican significativamente el código.



1. Función universal de búsqueda en un slice



// Encuentra el índice de un elemento en un slice. -1 si no se encuentra.func Index[T comparable](slice []T, target T                

Blogs

Book Recommendations