Generics em Go: guia completo sobre genéricos

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

Generics em Go: para que servem e como usar



Com o lançamento da versão Go 1.18, uma das funcionalidades mais esperadas apareceu na linguagem — os genéricos (programação genérica). Até este momento, os desenvolvedores tinham que duplicar código para diferentes tipos de dados ou usar a interface vazia interface{} com posterior type assertion. Os Generics resolvem este problema radicalmente. Neste artigo, vamos analisar para que servem, como usá-los na prática e quais armadilhas esperam por você.



O que são genéricos e para que servem em Go?



Genéricos (ou tipos genéricos) permitem escrever funções, estruturas e métodos que funcionam com qualquer tipo de dado, mantendo ao mesmo tempo a tipagem estrita. Isso significa que você pode escrever uma única função que funcionará tanto com números, quanto com strings, quanto com tipos personalizados, sem perder a informação sobre o tipo em tempo de compilação.



Antes do surgimento dos genéricos, os desenvolvedores Go usavam duas abordagens principais:



  • Duplicação de código — escrever funções separadas para int, string, float64, etc.
  • Interface vazia — usar interface{} com posterior type assertion ou reflexão, o que era inseguro e lento.


Os genéricos resolvem estes problemas, permitindo escrever código universal, mas ao mesmo tempo type-safe. O compilador verifica os tipos em tempo de compilação, o que elimina erros em tempo de execução.



Sintaxe dos Generics: type parameters



A ideia principal dos genéricos em Go são os type parameters. Eles são declarados entre colchetes após o nome da função ou tipo.



Vamos considerar um exemplo simples — uma função que retorna o mínimo de dois valores:



package main

import "fmt"

// Função genérica com type parameter Tfunc Min[T comparable](a, b T) T { if a < b { return a } return b}

func main() { fmt.Println(Min[int](10, 20)) // 10 fmt.Println(Min[string]("a", "b")) // "a" fmt.Println(Min(3.14, 2.71)) // 2.71 (tipo inferido automaticamente)}


Aqui T é o type parameter. comparable é uma constraint que diz que o tipo T deve suportar operadores de comparação. Sem esta constraint, o compilador não permitiria usar <.



Importante: Go pode inferir automaticamente os type parameters a partir dos argumentos, portanto na maioria dos casos você não precisa especificá-los explicitamente.



Constraints em genéricos Go



Constraints são um elemento chave dos genéricos. Elas definem quais operações são permitidas para os type parameters. Sem constraints, o compilador não sabe quais métodos ou operadores podem ser aplicados a T.



Em Go existem várias constraints embutidas:



  • any — sinônimo de interface{}, permite qualquer tipo.
  • comparable — permite tipos que suportam == e !=.
  • constraints.Ordered — do pacote golang.org/x/exp/constraints, permite todos os tipos ordenados (números e strings).


Você também pode criar suas próprias constraints usando interfaces:



package main

import "fmt"

// Constraint personalizada: o tipo deve ter o método String()type Stringer interface { String() string}

// Função que trabalha com qualquer Stringerfunc Print[T Stringer](value T) { fmt.Println(value.String())}

type User struct { Name string}

func (u User) String() string { return "User: " + u.Name}

func main() { user := User{Name: "Alice"} Print(user) // User: Alice}


Observe: em Go 1.18+, interfaces podem ser usadas como constraints, e elas podem conter não apenas métodos, mas também tipos concretos.

Blogs

Book Recommendations