Generics em Go: para que servem e como usar

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
```html

Introdução: o que são Generics e por que eles surgiram no Go



Por muito tempo, a comunidade Go lidou sem genéricos (tipos genéricos). Os desenvolvedores usavam interface{} e conversão manual de tipos, o que resultava em código verboso e erros em tempo de execução. Com o lançamento da versão Go 1.18 (fevereiro de 2022), os genéricos foram oficialmente adicionados à linguagem — um dos recursos mais esperados.



Generics (ou polimorfismo paramétrico) permitem escrever funções e estruturas de dados que funcionam com qualquer tipo, mantendo a tipagem estrita em tempo de compilação. Isso significa que você pode escrever um único algoritmo (por exemplo, ordenação ou busca) e usá-lo para int, string, structs personalizadas — sem duplicação de código e sem perder a segurança de tipos.



Neste artigo, vamos explorar por que os genéricos são necessários no Go, como usá-los corretamente e quais armadilhas devem ser consideradas.



Parte principal: como os Generics funcionam no Go



1. Sintaxe dos parâmetros de tipo



A sintaxe básica dos genéricos no Go é a seguinte: após o nome da função ou tipo, entre colchetes, são especificados os parâmetros de tipo. Vejamos um exemplo simples — uma função que retorna o valor recebido (análogo a identity):



package main

import "fmt"

// Função genérica: T é um parâmetro de tipofunc Print[T any](value T) { fmt.Println(value)}

func main() { Print[int](42) // especificação explícita de tipo Print[string]("Go") // especificação explícita de tipo Print("Go 1.18") // inferência implícita de tipo (type inference)}


A palavra-chave any é um alias para interface{}. Ela significa que a função aceita um valor de qualquer tipo. No entanto, o verdadeiro poder dos genéricos é revelado com o uso de restrições (constraints).



2. Restrições (constraints) e interfaces



As restrições permitem especificar quais operações podem ser realizadas com o parâmetro de tipo. Por exemplo, para somar dois números, é necessário garantir que o tipo suporte os operadores +, -, *, etc. No Go, isso é feito usando interfaces especiais do pacote golang.org/x/exp/constraints ou interfaces personalizadas:



package main

import ( "fmt" "golang.org/x/exp/constraints")

// Restrição: T deve suportar comparaçãofunc Max[T constraints.Ordered](a, b T) T { if a > b { return a } return b}

func main() { fmt.Println(Max(10, 20)) // 20 fmt.Println(Max("apple", "banana")) // banana // fmt.Println(Max([]int{1}, []int{2})) // Erro de compilação: slices não são ordenados}


A interface embutida constraints.Ordered inclui tipos que suportam os operadores <, <=, >, >=: todos os tipos numéricos e string.



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



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

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


3. Structs e métodos genéricos



Os genéricos no Go funcionam não apenas com funções, mas também com tipos personalizados. Vejamos um exemplo clássico — uma pilha genérica:



package main

import "fmt"

// Struct genérica Stacktype Stack[T any] struct { items []T}

// Push adiciona um elemento à pilhafunc (s *Stack[T]) Push(item T) { s.items = append(s.items, item)}

// Pop remove um elemento da pilhafunc (s *Stack[T]) Pop() (T, bool) { if len(s.items) == 0 { var zero T return zero, false } last := s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return last, true}

func main() ```

Blogs

Book Recommendations