Tudo começou com Go 1.18: por que os genéricos não são apenas um recurso da moda?
\n\nPor muito tempo, a comunidade Go se orgulhou da simplicidade da linguagem. A ausência de programação genérica (generics) era considerada uma escolha consciente: "não precisamos de templates complexos, escrevemos código compreensível". Mas a realidade falou mais alto. Os desenvolvedores se cansaram de copiar e colar funções para int, string e float64 — ou usar interface{} com conversão de tipos subsequente, perdendo segurança em tempo de compilação.
O lançamento do Go 1.18 (2022) foi uma revolução. Os genéricos (generics) adicionaram à linguagem parâmetros de tipo (type parameters) e restrições (constraints). Agora, programadores Go podem escrever estruturas de dados e algoritmos genéricos sem sacrificar desempenho e tipagem estrita.
\n\nNeste artigo no pythonlib.ru, vamos entender como os generics funcionam em Go, ver exemplos reais de código e descobrir quando usá-los — e quando não. Vamos lá!
\n\nO que são generics em Go em palavras simples?
\n\nImagine que você está escrevendo uma função que encontra o elemento mínimo em um slice. Sem genéricos, você teria que criar três versões: para int, float64 e string. Ou usar interface{} com conversão de tipos — lento e inseguro.
Generics permitem escrever uma função que funciona com qualquer tipo que satisfaça certas condições. A sintaxe é simples: após o nome da função, entre colchetes, são especificados os parâmetros de tipo.
\n\nSintaxe: colchetes e a palavra any
\n\npackage main\n\nimport "fmt"\n\n// Min — função genérica para encontrar o mínimo\nfunc Min[T any](a, b T) T {\n // Erro: operador < não definido para any\n // if a < b {\n // return a\n // }\n return b\n}\n\nO código acima não compila porque any é uma interface vazia (sinônimo de interface{}). O compilador não sabe se o tipo suporta o operador <. É necessário adicionar uma restrição (constraint).
Restrições de tipo: constraints
\n\nEm Go, as restrições são definidas por meio de interfaces. Você pode usar as embutidas do pacote golang.org/x/exp/constraints ou criar as suas próprias.
import (\n "fmt"\n "golang.org/x/exp/constraints"\n)\n\n// Min funciona com qualquer tipo ordenado\nfunc Min[T constraints.Ordered](a, b T) T {\n if a < b {\n return a\n }\n return b\n}\n\nfunc main() {\n fmt.Println(Min(10, 20)) // 10\n fmt.Println(Min(3.14, 2.71)) // 2.71\n fmt.Println(Min("apple", "banana")) // "apple"\n}\n\nObserve: constraints.Ordered inclui todos os tipos para os quais os operadores <, <=, >, >= são definidos — ou seja, int, float, string.
Exemplos práticos: do simples ao complexo
\n\nVamos ver como os generics em Go simplificam a escrita de código real.
\n\n1. Pilha genérica (Stack)
\n\nSem genéricos, você teria que armazenar []interface{} e fazer type assertion. Com generics — elegante e seguro.
package main\n\nimport "fmt"\n\n// Stack — estrutura de dados genérica\ntype Stack[T any] struct {\n items []T\n}\n\nfunc (s *Stack[T]) Push(item T) {\n s.items = append(s.items, item)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n if len(s.items) == 0 {\n var zero T\n return zero, false\n }\n item := s.items[len(s.items)-1]\n s.items = s.items[:len(s.items)-1]\n return item, true\n}\n\nfunc main() {\n intStack := Stack[int]{}\n intStack.Push(1)\n intStack.Push(2)\n val, ok := intStack.Pop()\n fmt.Println(val, ok) // 2 true\n\n strStack := Stack[string]{}\n strStack.Push("hello")\n