Las funciones de tipo generic
en Go nos permiten reutilizar un mismo código 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 si los números a sumar son ints
debemos sumarlos mediante la función SumInts
y si son floats
mediante la función 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, generic:
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