Go Básico: Generics
A linguagem Go nasceu sem o recurso conhecido como generics, que é uma forma mais "elegante" de criar novos tipos de parâmetros (exp: um parâmetro de uma função) que podem ser compostos de outros tipos.
Para ilustrar, podemos criar uma função que some a pontuação de todos os jogadores, representada em um mapa com seus nomes e sua pontuação final, que serão inicialmente números inteiros.
package main
import "fmt"
func main() {
scores := map[string]int{"User01": 100, "User02": 150, "User03": 50}
fmt.Printf("Final score: %d\n", AddScores(scores))
}
func AddScores(scores map[string]int) int {
var total int
for _, score := range scores {
total += score
}
return total
}
Agora, vamos imaginar que precisamos lidar não apenas com o nosso mapa que representa os pontos em números inteiros, mas também com um novo mapa que representa a pontuação com números decimais.
Essa simples alteração faria com que nossa função AddScores precisasse ser duplicada e resultaria em uma reescrita considerável de código, como podemos ver no exemplo.
package main
import "fmt"
func main() {
scoresA := map[string]int{"User01": 100, "User02": 150, "User03": 50}
scoresB := map[string]float64{"User10": 100.0, "User20": 150.0, "User30": 50.0}
fmt.Printf("Final score A: %d\n", AddScoresInt(scoresA))
fmt.Printf("Final score B: %.2f\n", AddScoresFloat(scoresB))
}
func AddScoresInt(scores map[string]int) int {
var total int
for _, score := range scores {
total += score
}
return total
}
func AddScoresFloat(scores map[string]float64) float64 {
var total float64
for _, score := range scores {
total += score
}
return total
}
É comum nos depararmos com códigos que adotam alternativas para evitar a redundância de funções. Um exemplo disso é o uso de técnicas com a interface vazia (interface{}), que é capaz de representar todos os tipos existentes. Dentro da função, ocorre a devida manipulação e conversão para desconsiderar os tipos indesejados.
Entretanto, a partir da versão 1.18 do Go, foram adicionadas algumas ferramentas específicas para lidar com essas situações, como demonstrado no exemplo a seguir.
package main
import "fmt"
func main() {
scoresA := map[string]int{"User01": 100, "User02": 150, "User03": 50}
scoresB := map[string]float64{"User10": 100.0, "User20": 150.0, "User30": 50.0}
fmt.Printf("Final score A: %d\n", AddScores(scoresA))
fmt.Printf("Final score B: %.2f\n", AddScores(scoresB))
}
// New type T that is int or float64
func AddScores[T int | float64](scores map[string]T) T {
var total T
for _, score := range scores {
total += score
}
return total
}
Para tornar ainda mais interessante, podemos utilizar um recurso chamado constraint, que é outra forma de criar o "T int | float64" de maneira que possamos reutilizá-lo em outros lugares.
package main
import "fmt"
// New constraint Score that is int or float64
type Score interface {
int | float64
}
func main() {
scoresA := map[string]int{"User01": 100, "User02": 150, "User03": 50}
scoresB := map[string]float64{"User10": 100.0, "User20": 150.0, "User30": 50.0}
fmt.Printf("Final score A: %d\n", AddScores(scoresA))
fmt.Printf("Final score B: %.2f\n", AddScores(scoresB))
}
// New type T that is int or float64
func AddScores[T Score](scores map[string]T) T {
var total T
for _, score := range scores {
total += score
}
return total
}
Isso foi apenas um exemplo simples do que podemos fazer com generics, porém existem diversos outros recursos interessantes para generics, como o uso de comparable types e outros.
Comentários
Postar um comentário