Saltar al contenido principal
eLearner.app
Módulo 10 · Lección 3 de 548/50 en el curso~15 min
Lecciones del módulo (3/5)

Genéricos (Go 1.18+)

Desde Go 1.18 (marzo de 2022), parámetros de tipo están disponibles: las funciones y los tipos se pueden parametrizar sobre los tipos. Le permiten escribir Map, Filter, Set[T], LinkedList[T] sin duplicar código y sin interface{} + afirmaciones de tipo de tiempo de ejecución.

Función genérica: la sintaxis

Go
func Map[T, U any](s []T, f func(T) U) []U {
    out := make([]U, len(s))
    for i, v := range s {
        out[i] = f(v)
    }
    return out
}

doubled := Map([]int{1, 2, 3}, func(n int) int { return n * 2 })
// doubled is []int{2, 4, 6}

Tres nuevos elementos:

  • [T, U any] después del nombre: declara los parámetros de tipo con sus restricciones.
  • any = restricción que acepta cualquier tipo (alias de interface{} desde Go 1.18).
  • Inferencia: al llamar a Map([]int{...}, ...) no es necesario escribir Map[int, int](...): el compilador infiere T=int, U=int.

A veces la inferencia no es suficiente (por ejemplo, constructores sin argumentos T) y se necesita una instanciación explícita: New[*Server]().

Restricciones incorporadas

RestricciónSignificado
CÓDIGOPH0Cualquier tipo (== CÓDIGOPH1).
CÓDIGOPH2Tipos con == y !=: numérico, cadena, bool, punteros, canales, matrices/estructuras comparables.

comparable permite la igualdad pero no < o >.

Go
func Index[T comparable](s []T, x T) int {
    for i, v := range s {
        if v == x { return i }
    }
    return -1
}

Para </> necesita una restricción personalizada.

Restricción personalizada: uniones de tipos

Una restricción es una interfaz con cláusulas de "elemento de tipo":

Go
type Ordered interface {
    ~int | ~int64 | ~float64 | ~string
}

func Min[T Ordered](a, b T) T {
    if a < b { return a }
    return b
}
  • | = unión: acepta cualquiera de los tipos enumerados.
  • ~int = "int o cualquier tipo definido como type X int" (con el mismo tipo subyacente). Sin ~, sólo int exacto.

Una restricción también puede mezclar métodos y elementos de tipo:

Go
type Stringable interface {
    ~string
    Len() int
}

Tipos genéricos

No solo funciones: las estructuras y las interfaces también pueden tener parámetros de tipo.

Go
type Set[T comparable] struct {
    m map[T]struct{}
}

func NewSet[T comparable]() *Set[T] {
    return &Set[T]{m: make(map[T]struct{})}
}

func (s *Set[T]) Add(v T) { s.m[v] = struct{}{} }
func (s *Set[T]) Has(v T) bool { _, ok := s.m[v]; return ok }

s := NewSet[string]()
s.Add("ada")

Los métodos NO PUEDEN agregar parámetros de tipo más allá de los del tipo: func (s *Set[T]) MapTo[U any](...) no compila (limitación de diseño intencional).

Cuándo usar genéricos

cuando:

  • La lógica es verdaderamente idéntica para varios tipos (colecciones, algoritmos, ayudantes como Map/Filter/Reduce).
  • La alternativa sería duplicar código o usar afirmaciones de tipo interface{} + (perdiendo seguridad de tipo).

No cuando:

  • Una interfaz con uno o dos métodos es suficiente y más legible (io.Reader, fmt.Stringer).
  • Estás parametrizando "porque puedes": un caso de uso único → haz una versión concreta del mismo.

Ejercicios

Ejercicio#go.m10.l3.e1
Intentos: 0Cargando...

Definisci la funzione generica Min[T Ordered](a, b T) T che ritorna il minore usando el operador <. La restricción Ordenada es già fornito.

Cargando editor...

Solución disponible después de 3 intentos

Ejercicio#go.m10.l3.e2
Intentos: 0Cargando...

Implementa Map[T, U any](s []T, f func(T) U) []U: aplica f a cada elemento y restituye el nuevo segmento.

Cargando editor...

Solución disponible después de 3 intentos

Cuestionario#go.m10.l3.e3
Listo

Cual restricción incorporada acepta el operador == ma NON el operador <?

Go
func Index[T ???](s []T, x T) int { ... }
Opciones de respuesta