Generics em Go: para que servem e como usar para iniciantes

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

Introdução: o que são Generics e por que surgiram em Go?

\n

Por muito tempo, a linguagem Go (Golang) permaneceu como uma das poucas linguagens modernas sem suporte a programação genérica (generics). Os desenvolvedores precisavam duplicar código para diferentes tipos de dados ou usar interfaces e interface{} vazios, o que levava à perda de segurança de tipos e à necessidade de realizar conversões de tipos em tempo de execução.

\n

Com o lançamento da versão Go 1.18 (fevereiro de 2022), a linguagem finalmente recebeu os tão esperados genéricos. Generics é um mecanismo que permite escrever funções, tipos e estruturas de dados que funcionam com tipos arbitrários, mantendo a tipagem estrita em tempo de compilação.

\n

Neste artigo, abordaremos:

\n
    \n
  • Para que servem os genéricos em Go;
  • \n
  • Como declarar funções e tipos genéricos;
  • \n
  • O que são constraints e como usá-las;
  • \n
  • Exemplos práticos de aplicação.
  • \n
\n\n

Para que servem os Generics em Go?

\n

Antes dos genéricos, um problema típico era: escrever uma função que trabalhasse com slices de diferentes tipos (por exemplo, int, string, float64).

\n

Sem genéricos, era necessário escrever três funções separadas:

\n
func SumInts(nums []int) int {\n    total := 0\n    for _, v := range nums {\n        total += v\n    }\n    return total\n}\n\nfunc SumFloats(nums []float64) float64 {\n    total := 0.0\n    for _, v := range nums {\n        total += v\n    }\n    return total\n}\n
\n

Ou usar interface{} com conversão de tipos — inseguro e inconveniente.

\n

Com genéricos, basta uma única função:

\n
func Sum[T int | float64](nums []T) T {\n    var total T\n    for _, v := range nums {\n        total += v\n    }\n    return total\n}\n
\n

As vantagens são óbvias:

\n
    \n
  • Segurança de tipos — erros de tipo são detectados em tempo de compilação;
  • \n
  • Redução de duplicação de código — uma função para muitos tipos;
  • \n
  • Legibilidade — o código se torna mais conciso e claro.
  • \n
\n\n

Fundamentos da sintaxe de Generics em Go

\n

Declaração de uma função genérica

\n

A sintaxe básica usa colchetes [] para especificar os parâmetros de tipo:

\n
func NomeDaFunção[NomeDoTipo constraint](parâmetros) resultado {\n    // corpo da função\n}\n
\n

Exemplo de uma função simples que retorna o valor recebido:

\n
func Identity[T any](value T) T {\n    return value\n}\n\nfunc main() {\n    fmt.Println(Identity[int](42))       // 42\n    fmt.Println(Identity[string]("Go"))  // Go\n}\n
\n

Go pode inferir automaticamente o tipo, portanto a especificação explícita [int] é opcional:

\n
fmt.Println(Identity(42))       // Go entende que T = int\nfmt.Println(Identity("Go"))    // T = string\n
\n\n

Tipos genéricos (structs)

\n

Os genéricos funcionam não apenas com funções, mas também com tipos personalizados:

\n
type Stack[T any] struct {\n    elements []T\n}\n\nfunc (s *Stack[T]) Push(value T) {\n    s.elements = append(s.elements, value)\n}\n\nfunc (s *Stack[T]) Pop() (T, bool) {\n    if len(s.elements) == 0 {\n        var zero T\n        return zero, false\n    }\n    value := s.elements[len(s.elements)-1]\n    s.elements = s.elements[:len(s.elements)-1]\n    return value, true\n}\n\nfunc main() {\n    intStack := Stack[int]{}\n    intStack.Push(10)\n    intStack.Push(20)\n    val, ok := intStack.Pop()\n    fmt.Println(val, ok) // 20 true\n}\n
\n\n

Constraints: restrições para tipos

\n

Nos exemplos acima, usamos any — uma constraint embutida que significa "qualquer tipo". No entanto, muitas vezes é necessário impor restrições: por exemplo, que o tipo suporte comparação ou operações aritméticas.

\n

Embutidas

Blogs

Book Recommendations