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?



Por muito tempo, a comunidade Go (Golang) viveu sem genéricos (programação genérica). Os desenvolvedores usavam interfaces, interface{} vazios e geração de código para contornar as limitações da tipagem estrita. Na versão Go 1.18 (fevereiro de 2022), a linguagem finalmente recebeu genéricos — uma ferramenta poderosa que permite escrever funções e estruturas que funcionam com diferentes tipos de dados sem perder a segurança de tipos.



Neste artigo, vamos entender para que servem os Generics em Go, como funcionam e aprender a aplicá-los na prática. O material é voltado para desenvolvedores iniciantes que já conhecem os fundamentos de Go: variáveis, funções e slices.



Para que servem os genéricos em Go?



Imagine que você precisa escrever uma função que encontra o valor mínimo em um slice de números. Sem genéricos, você teria que escrever funções separadas para int, float64 e outros tipos:



// Sem genéricos — três funções idênticasfunc MinInt(a, b int) int {    if a < b {        return a    }    return b}

func MinFloat64(a, b float64) float64 { if a < b { return a } return b}


Isso leva à duplicação de código. Antes, esse problema era resolvido com interface{} e conversão de tipos, mas essa abordagem perdia a segurança em tempo de compilação:



func Min(a, b interface{}) interface{} {    // Comparação e conversão — fácil errar    return a}


Os genéricos resolvem três problemas principais:

  • Segurança de tipos — erros de tipo são detectados em tempo de compilação.
  • Eliminação de duplicação — uma função funciona com qualquer tipo.
  • Legibilidade do código — indicação explícita de quais tipos são suportados.


Fundamentos da sintaxe: type parameters e constraints



Os genéricos em Go são baseados em dois conceitos: parâmetros de tipo (type parameters) e restrições (constraints).



Parâmetros de tipo



Os parâmetros de tipo são especificados entre colchetes após o nome da função ou tipo. Por convenção, geralmente se usa a letra T (de Type), mas qualquer nome pode ser usado:



// Função com um parâmetro de tipo Tfunc Print[T any](value T) {    fmt.Println(value)}

// UsoPrint[int](42) // especificamos o tipo explicitamentePrint("Hello") // o compilador infere o tipo automaticamente


A palavra-chave any é um alias para a interface vazia interface{}. Ela significa que a função aceita qualquer tipo.



Restrições (constraints)



As restrições definem quais tipos podem ser usados com um genérico. Por exemplo, para comparar números, o tipo precisa suportar os operadores < e >. As restrições embutidas para tipos numéricos estão no pacote golang.org/x/exp/constraints:



import "golang.org/x/exp/constraints"

// Restrição — apenas números inteirosfunc Sum[T constraints.Integer](numbers []T) T { var total T for _, n := range numbers { total += n } return total}


Você também pode criar suas próprias restrições usando interfaces:



// Restrição personalizada: o tipo deve ter um método String()type Stringer interface {    String() string}

func Describe[T Stringer](value T) string { return "Value: " + value.String()}


Exemplos práticos de uso de Generics



Vamos ver alguns cenários reais onde os genéricos simplificam significativamente o código.



1. Função universal de busca em slice



// Encontra o índice de um elemento em um slice. -1 se não encontrado.func Index[T comparable](slice []T, target T                

Blogs

Book Recommendations