Generics mudaram Go para sempre quando chegaram no Go 1.18. Mas a historia nao parou ali. De la para ca, cada release trouxe melhorias que tornaram generics mais poderosos, mais simples de usar e mais integrados ao dia a dia do desenvolvedor. Neste artigo, vamos percorrer a evolucao dos generics do Go 1.24 ate o Go 1.26, com exemplos praticos que voce pode aplicar nos seus projetos hoje.
Se voce ainda esta comecando com generics, recomendo antes ler nosso guia pratico de generics e a entrada do glossario sobre generic.
Go 1.24: Generic Type Aliases Finalmente Completos
O Go 1.24, lancado em fevereiro de 2025, trouxe uma das mudancas mais esperadas: generic type aliases completos. Antes, voce podia criar aliases simples com type MinhaString = string, mas nao era possivel parametrizar aliases com tipos genericos. Isso mudou.
Como Funcionam os Generic Type Aliases
Agora voce pode criar aliases que aceitam parametros de tipo:
package collections
// Alias generico para um slice ordenado
type SortedSlice[T constraints.Ordered] = []T
// Alias para um map com chaves string
type StringMap[V any] = map[string]V
Isso e particularmente util quando voce precisa migrar APIs gradualmente. Imagine que voce tem um pacote legado e quer expor tipos novos sem quebrar compatibilidade:
// Pacote antigo exportava OldList
// Agora NewList e o tipo principal, mas OldList continua funcionando
type OldList[T any] = NewList[T]
Weak Pointers e Finalizers Melhorados
O Go 1.24 tambem introduziu o pacote unique com weak pointers – ponteiros que nao impedem o garbage collector de liberar a memoria. Combinados com o novo runtime.AddCleanup (que substitui runtime.SetFinalizer), voce ganha controle mais fino sobre o ciclo de vida de objetos:
package main
import (
"fmt"
"runtime"
"unique"
)
func main() {
// Handle unico -- se dois valores sao iguais,
// retorna o mesmo handle (internalizacao)
h1 := unique.Make("golang")
h2 := unique.Make("golang")
fmt.Println(h1 == h2) // true -- mesmo handle
// AddCleanup substitui SetFinalizer com semantica mais clara
obj := &MyResource{name: "conexao-db"}
runtime.AddCleanup(obj, func(name string) {
fmt.Printf("Recurso %s liberado\n", name)
}, obj.name)
}
type MyResource struct {
name string
}
A vantagem do AddCleanup sobre o antigo SetFinalizer e que ele nao impede o garbage collector de coletar o objeto e permite multiplos cleanups no mesmo objeto.
Go 1.25: Adeus Core Types
O Go 1.25 trouxe a mudanca mais significativa para generics desde o Go 1.18: a remocao completa do conceito de “core types”. Se voce ja tentou usar operacoes como range ou channel sends dentro de funcoes genericas e recebeu erros confusos, sabe do que estou falando.
O Problema dos Core Types
No Go 1.18-1.24, o compilador exigia que interfaces usadas como constraints tivessem um “core type” – essencialmente, todos os tipos no type set precisavam ter a mesma estrutura subjacente. Isso gerava restricoes frustrantes:
// Antes do Go 1.25 -- ERRO: nao tem core type
type Iterable interface {
[]int | []string // tipos diferentes, sem core type
}
func Print[T Iterable](s T) {
for _, v := range s { // erro de compilacao!
fmt.Println(v)
}
}
A Solucao no Go 1.25+
Com a remocao dos core types, o compilador ficou mais inteligente. Agora ele analisa cada operacao individualmente e verifica se todos os tipos no type set suportam aquela operacao:
// Go 1.25+ -- funciona perfeitamente
type Number interface {
int | float64 | int64
}
func Sum[T Number](values []T) T {
var total T
for _, v := range values {
total += v
}
return total
}
Essa mudanca simplificou a spec da linguagem e abriu portas para patterns mais expressivos com range e operadores dentro de funcoes genericas.
Go 1.26: Self-Referencing Generics
O Go 1.26, lancado em fevereiro de 2026, deu mais um passo importante: tipos genericos agora podem se referenciar na propria lista de parametros de tipo. Isso desbloqueia patterns como:
// Self-referencing -- o tipo T referencia a si mesmo
type Comparable[T interface{ Compare(T) int }] struct {
Value T
}
// Implementacao concreta
type Score struct {
Points int
}
func (s Score) Compare(other Score) int {
return s.Points - other.Points
}
func Max[T interface{ Compare(T) int }](a, b T) T {
if a.Compare(b) > 0 {
return a
}
return b
}
func main() {
result := Max(Score{100}, Score{85})
fmt.Println(result.Points) // 100
}
Esse pattern e essencial para estruturas de dados como arvores binarias de busca, grafos e listas encadeadas genericas – algo que antes exigia workarounds com interfaces vazias ou conversoes de tipo.
Exemplo Pratico: Arvore Binaria Generica
type TreeNode[T interface{ Compare(T) int }] struct {
Value T
Left *TreeNode[T]
Right *TreeNode[T]
}
func (n *TreeNode[T]) Insert(val T) *TreeNode[T] {
if n == nil {
return &TreeNode[T]{Value: val}
}
if val.Compare(n.Value) < 0 {
n.Left = n.Left.Insert(val)
} else {
n.Right = n.Right.Insert(val)
}
return n
}
Se voce trabalha com structs e ponteiros no dia a dia, esse tipo de codigo generico elimina toneladas de duplicacao.
Usando Generics na Pratica com slices, maps e cmp
Alem das mudancas na linguagem, os pacotes slices, maps e cmp da standard library evoluiram bastante. Aqui estao patterns que todo desenvolvedor Go deveria conhecer em 2026:
package main
import (
"cmp"
"fmt"
"slices"
"maps"
)
func main() {
// slices.SortFunc com cmp.Compare
nums := []int{42, 7, 13, 99, 1}
slices.SortFunc(nums, cmp.Compare)
fmt.Println(nums) // [1 7 13 42 99]
// slices.Contains
fmt.Println(slices.Contains(nums, 13)) // true
// maps.Keys e maps.Values (Go 1.24+)
config := map[string]int{
"timeout": 30,
"retries": 3,
"workers": 8,
}
for _, key := range slices.Sorted(maps.Keys(config)) {
fmt.Printf("%s: %d\n", key, config[key])
}
}
Esses pacotes eliminam a necessidade de funcoes utilitarias que todo projeto Go costumava ter. Se voce usa map e slice frequentemente, explore essas APIs – elas sao muito mais seguras que loops manuais.
Impacto na Comunidade Brasileira
A pesquisa de desenvolvedores Go de 2025 mostrou que generics ja e uma das features mais adotadas. No Brasil, frameworks como Fiber e bibliotecas como sqlc estao adotando generics em suas APIs. Se voce esta acompanhando o mercado de trabalho Go, saber usar generics de forma idiomatica ja e requisito em muitas vagas.
Para quem trabalha com concorrencia e channels, generics tambem simplificam patterns como fan-out/fan-in e pipelines tipados – temas que cobrimos no artigo sobre padroes de concorrencia.
Desenvolvedores que usam Kotlin ou Python vao notar que os generics do Go seguem uma filosofia diferente: menos magica, mais explicitacao e seguranca de tipos em tempo de compilacao.
Perguntas Frequentes
Generics do Go sao mais lentos que codigo sem generics?
Na maioria dos casos, nao. O compilador Go usa uma combinacao de stenciling (geracao de codigo especializado) e dicionarios para implementar generics. Desde o Go 1.21, as otimizacoes de PGO (Profile-Guided Optimization) tambem beneficiam funcoes genericas. Em benchmarks reais, a diferenca de performance e negligivel.
Preciso reescrever meu codigo para usar generics?
Nao. Generics sao uma ferramenta a mais, nao uma substituicao. Use quando voce tem duplicacao real de logica entre tipos diferentes. Funcoes que operam em um unico tipo concreto nao precisam ser genericas. O principio e: se voce esta copiando e colando codigo mudando apenas o tipo, generics e a solucao certa.
O que muda dos generic type aliases do Go 1.24 para o Go 1.26?
No Go 1.24, generic type aliases foram introduzidos como feature completa. No Go 1.25, a remocao dos core types simplificou como constraints funcionam. No Go 1.26, self-referencing generics permitiram patterns recursivos. Cada versao construiu sobre a anterior sem quebrar compatibilidade.
Quais pacotes da standard library usam generics?
Os principais sao slices, maps, cmp e unique. Alem deles, pacotes como sync (com sync.Map generico no futuro) e sort estao gradualmente adotando generics. Voce pode explorar mais sobre funcoes e interfaces genericas nos nossos glossarios.