Introdução: Por que Generics em Go são um avanço?
Por muito tempo, a linguagem Go (Golang) permaneceu uma das poucas linguagens modernas sem suporte para programação genérica (generics). Os desenvolvedores eram forçados a usar interface{} e conversão de tipos, o que levava à perda de segurança de tipos em tempo de compilação e poluía o código. Com o lançamento da versão Go 1.18 (fevereiro de 2022), a situação mudou drasticamente.
Generics (genéricos) é um mecanismo que permite escrever funções e estruturas de dados que podem trabalhar com qualquer tipo, mantendo a tipagem estrita. Neste artigo, vamos explorar para que servem e mostrar com exemplos práticos como usá-los no desenvolvimento diário com Go.
Para que servem os Generics em Go?
Antes dos genéricos, para criar funções universais, os desenvolvedores usavam interface{} (interface vazia). Vejamos um exemplo clássico — uma função que encontra o elemento mínimo em um slice:
// Sem genéricos (Go < 1.18)func MinInt(a, b int) int { if a < b { return a } return b}
func MinFloat(a, b float64) float64 { if a < b { return a } return b}Para cada tipo, era necessário escrever uma função separada. Com interface{}, era possível fazer assim:
func MinInterface(a, b interface{}) interface{} { // Requer conversão de tipos e verificação em tempo de execução // Isso é inseguro e ineficiente}Os problemas dessa abordagem são óbvios:
- Perda de segurança de tipos — o erro pode aparecer apenas em tempo de execução.
- Necessidade de conversão de tipos — o código se torna verboso.
- Redução de desempenho — trabalhar com interfaces exige operações adicionais.
Generics resolvem todos esses problemas. Eles permitem:
- Escrever algoritmos universais uma única vez.
- Manter a verificação estrita de tipos em tempo de compilação.
- Eliminar a conversão de tipos e melhorar o desempenho.
Fundamentos da sintaxe de Generics em Go
Os genéricos em Go são baseados em parâmetros de tipo. A sintaxe é simples e intuitiva. Os parâmetros de tipo são indicados entre colchetes após o nome da função ou tipo.
1. Função simples com genéricos
Vamos escrever a mesma função de mínimo, mas agora usando genéricos:
package main
import "fmt"
// Min retorna o valor mínimo entre doisfunc Min[T comparable](a, b T) T { if a < b { return a } return b}
func main() { fmt.Println(Min[int](10, 20)) // 10 fmt.Println(Min[float64](3.14, 2.71)) // 2.71 fmt.Println(Min[string]("abc", "xyz")) // abc}Aqui, [T comparable] é o parâmetro de tipo. comparable é uma restrição (constraint) embutida que garante que o tipo suporta os operadores de comparação == e !=. Observe: para o operador <, é necessário usar a restrição constraints.Ordered (do pacote golang.org/x/exp/constraints) ou criar a sua própria.
2. Restrições (Constraints)
Restrições são interfaces que definem quais métodos e operações os tipos devem suportar. No Go 1.18+, foi introduzido o pacote constraints (ainda em golang.org/x/exp), mas você pode criar suas próprias restrições.
import "golang.org/x/exp/constraints"
// MinOrdered funciona apenas com tipos ordenadosfunc MinOrdered[T constraints.Ordered](a, b T) T { if a < b { return a } return b}Restrição personalizada para tipos que suportam o método String():
Blogs
Book Recommendations
Leia também
Funções em Python: declaração, argumentos, retorno de valores e variáveis globais
GraphQL API com C++: guia passo a passo para criar um servidor