Esta pagina se ve mejor con JavaScript habilitado

Go generics

 ·  🎃 kr0m

Las funciones de tipo generic en Go nos permiten reutilizar una misma función para dos tipos de entradas distintas, en este artículo explicaremos con un sencillo ejemplo el modo de funcionamiento.


Si eres nuevo en el mundo de Go te recomiendo los siguientes artículos anteriores:


Primero veamos un ejemplo sin funciones generic en el que disponemos de dos funciones donde se les pasa un map y suman los valores de este. Estos valores pueden ser tanto números enteros como coma flotante, por lo tanto necesitaremos dos funciones distintas SumInts y SumFloats.

vi generics.go
package main

import "fmt"

// SumInts adds together the values of m.
func SumInts(m map[string]int64) int64 {
    var s int64
    for _, v := range m {
        s += v
    }
    return s
}

// SumFloats adds together the values of m.
func SumFloats(m map[string]float64) float64 {
    var s float64
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Non-Generic Sums: %v and %v\n",
        SumInts(ints),
        SumFloats(floats))
}

Ejecutamos el código:

go run generics.go

Non-Generic Sums: 46 and 62.97

Para hacer una función genérica SumIntsOrFloats, además de los parámetros de entrada y retorno de la función, también debemos indicar los type arguments permitiendo que pueda operar con los tipos de parámetros de entrada indicados.

Comparemos las funciones normales con la generic:

func SumInts(m map[string]int64) int64 {
func SumFloats(m map[string]float64) float64 {
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {

Como podemos ver las dos primeras aceptan la variable de entrada m de tipo map(m map[string]int64 / m map[string]float64), donde:

  • La key es de tipo String.
  • El value de tipo int64/float64 respectivamente.
  • Los valores de retorno int64/float64 respectivamente.

En cambio en la función generic se recibe como entrada un parámetro de tipo map(m map[K]V), donde:

  • La key es de tipo comparable(K comparable), este tipo permite cualquier valor que pueda ser comparado mediante ==, !=. Go requiere que las map keys sean comparable .
  • El value de tipo int64 o float64(V int64 | float64).
  • El valor de retorno de tipo int64 o float64 ya que V es (V int64 | float64).

Debemos tener en cuenta que no podemos restringir los type arguments a cualquier grupo de valores, esto depende de las operaciones que realice el código, por ejemplo si permitimos en los type arguments enteros y strings pero luego el código intenta hacer sumas con los strings, el código directamente no compilará.

La llamada desde main a la función generic también cambia, debemos indicarle el tipo de datos de los parámetros de entrada, comparemos de nuevo el código inicial con el actual:

SumInts(ints),
SumFloats(floats),
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))

El código final utilizando generics sería el siguiente:

vi generics2.go

package main

import "fmt"

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

func main() {
    // Initialize a map for the integer values
    ints := map[string]int64{
        "first":  34,
        "second": 12,
    }

    // Initialize a map for the float values
    floats := map[string]float64{
        "first":  35.98,
        "second": 26.99,
    }

    fmt.Printf("Generic Sums: %v and %v\n",
        SumIntsOrFloats[string, int64](ints),
        SumIntsOrFloats[string, float64](floats))
}

Podemos ver que al ejecutarlo obtenemos exactamente el mismo resultado pero con menos líneas de código:

go run generics2.go

Generic Sums: 46 and 62.97
Si te ha gustado el artículo puedes invitarme a un RedBull aquí