Genéricos em Go: como a programação genérica está mudando as regras do jogo

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Tudo começou com Go 1.18: por que os genéricos não são apenas um recurso da moda?

\n\n

Por 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.

\n\n

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\n

Neste 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\n

O que são generics em Go em palavras simples?

\n\n

Imagine 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.

\n\n

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\n

Sintaxe: colchetes e a palavra any

\n\n
package 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\n

O 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).

\n\n

Restrições de tipo: constraints

\n\n

Em 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.

\n\n
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\n

Observe: constraints.Ordered inclui todos os tipos para os quais os operadores <, <=, >, >= são definidos — ou seja, int, float, string.

\n\n

Exemplos práticos: do simples ao complexo

\n\n

Vamos ver como os generics em Go simplificam a escrita de código real.

\n\n

1. Pilha genérica (Stack)

\n\n

Sem genéricos, você teria que armazenar []interface{} e fazer type assertion. Com generics — elegante e seguro.

\n\n
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                

Blogs

Book Recommendations