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
.
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:
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:
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:
Generic Sums: 46 and 62.97