Introducción
Esta práctica introduce el concepto de genéricos en Golang. A partir de la versión 1.18, Golang ha agregado soporte para genéricos, lo que nos permite escribir código más flexible y reutilizable.
This tutorial is from open-source community. Access the source code
💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí
Esta práctica introduce el concepto de genéricos en Golang. A partir de la versión 1.18, Golang ha agregado soporte para genéricos, lo que nos permite escribir código más flexible y reutilizable.
El problema que se debe resolver en esta práctica es entender cómo definir y usar funciones y tipos genéricos en Golang.
$ go run generics.go
keys: [4 1 2]
list: [10 13 23]
A continuación se muestra el código completo:
// A partir de la versión 1.18, Go ha agregado soporte para
// _genéricos_, también conocidos como _parámetros de tipo_.
package main
import "fmt"
// Como ejemplo de una función genérica, `MapKeys` toma
// un mapa de cualquier tipo y devuelve una lista de sus claves.
// Esta función tiene dos parámetros de tipo - `K` y `V`;
// `K` tiene la restricción _comparable_, lo que significa que
// podemos comparar valores de este tipo con los operadores `==` y
// `!=`. Esto es necesario para las claves de mapa en Go.
// `V` tiene la restricción `any`, lo que significa que no está
// restringido de ninguna manera (`any` es un alias para `interface{}`).
func MapKeys[K comparable, V any](m map[K]V) []K {
r := make([]K, 0, len(m))
for k := range m {
r = append(r, k)
}
return r
}
// Como ejemplo de un tipo genérico, `List` es una
// lista simplemente enlazada con valores de cualquier tipo.
type List[T any] struct {
head, tail *element[T]
}
type element[T any] struct {
next *element[T]
val T
}
// Podemos definir métodos en tipos genéricos igual que lo hacemos
// en tipos normales, pero tenemos que mantener los parámetros de tipo
// en su lugar. El tipo es `List[T]`, no `List`.
func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}
func (lst *List[T]) GetAll() []T {
var elems []T
for e := lst.head; e!= nil; e = e.next {
elems = append(elems, e.val)
}
return elems
}
func main() {
var m = map[int]string{1: "2", 2: "4", 4: "8"}
// Cuando invocamos funciones genéricas, a menudo podemos confiar
// en la _inferencia de tipo_. Tenga en cuenta que no tenemos que
// especificar los tipos para `K` y `V` cuando
// llamamos a `MapKeys` - el compilador los infiere
// automáticamente.
fmt.Println("keys:", MapKeys(m))
//... aunque también podríamos especificarlos explícitamente.
_ = MapKeys[int, string](m)
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)
fmt.Println("list:", lst.GetAll())
}
Los genéricos son una característica poderosa que nos permite escribir código más flexible y reutilizable en Golang. Con los genéricos, podemos definir funciones y tipos que funcionen con cualquier tipo, siempre y cuando cumplan ciertas restricciones. Al usar genéricos, podemos reducir la duplicación de código y mejorar la legibilidad y mantenibilidad del código.