Finalmente está (quase) entre nós! Depois de anos ouvindo aquela piadinha "e o Generics?" esta aguardada funcionalidade vai estar disponível na versão 1.18 da linguagem, prevista para lançamento em Março de 2022.
Neste post eu vou fazer um exemplo usando Generics e um pequeno benchmark para conferir se existem diferenças de performance entre uma função "normal" e outra usando esta nova funcionalidade.
Para isso eu me inspirei na biblioteca lo, uma das primeiras que usa Generics e que ganhou destaque recentemente por implementar várias funcionalidades úiteis para slices e maps.
O primeiro passo foi instalar o Go 1.18, que no momento da escrita deste post encontra-se na versão Release Canditate 1. Para isso eu segui essa documentação e executei os comandos:
go install golang.org/dl/go1.18rc1@latest go1.18rc1 download Com isso foi criado o diretório sdk na home do meu usuário no Mac. Vamos usar esse diretório para configurar a IDE, para que ela reconheça a nova versão da linguagem. Eu estou usando o Goland da Jetbrains, então minha configuração ficou desta forma:
Além de criar o diretório sdk os comandos acima criaram o binário go1.18rc1 no diretório go/bin da home do meu usuário no Mac. É esse binário que vamos usar para rodar os testes:
eminetto@MacBook-Pro-da-Trybe ~/D/post-generics [1]> go1.18rc1 version go version go1.18rc1 darwin/arm64 O próximo passo foi criar um diretório e um main.go:
mkdir post-generics cd post-generics go1.18rc1 mod init github.com/eminetto/post-generics touch main.go No main.go eu escrevi o seguinte código:
package main import ( "fmt" ) func main() { s := []string{"Samuel", "Marc", "Samuel"} names := Uniq(s) fmt.Println(names) names = UniqGenerics(s) fmt.Println(names) i := []int{1, 20, 20, 10, 1} ids := UniqGenerics(i) fmt.Println(ids) } //from https://github.com/samber/lo/blob/master/slice.go func UniqGenerics[T comparable](collection []T) []T { result := make([]T, 0, len(collection)) seen := make(map[T]struct{}, len(collection)) for _, item := range collection { if _, ok := seen[item]; ok { continue } seen[item] = struct{}{} result = append(result, item) } return result } func Uniq(collection []string) []string { result := make([]string, 0, len(collection)) seen := make(map[string]struct{}, len(collection)) for _, item := range collection { if _, ok := seen[item]; ok { continue } seen[item] = struct{}{} result = append(result, item) } return result } Na função main é possível ver a principal vantagem de Generics, pois usamos a mesma função para remover as entradas duplicadas em slices de strings e de inteiros, sem a necessidade de alteração de código.
Ao executar o código podemos ver o resultado:
eminetto@MacBook-Pro-da-Trybe ~/D/post-generics> go1.18rc1 run main.go [Samuel Marc] [Samuel Marc] [1 20 10] Mas e quanto a performance? Estamos perdendo algo ao adicionar essa nova funcionalidade? Para tentar responder isso eu fiz um pequeno benchmark. O primeiro passo foi instalar o pacote faker, para gerar mais dados para o benchmark:
go1.18rc1 get -u github.com/bxcodec/faker/v3 E o código do main_test.go ficou desta forma:
package main import ( "github.com/bxcodec/faker/v3" "testing" ) var names []string func BenchmarkMain(m *testing.B) { for i := 0; i < 1000; i++ { names = append(names, faker.FirstName()) } } func BenchmarkUniq(b *testing.B) { _ = Uniq(names) } func BenchmarkGenericsUniq(b *testing.B) { _ = UniqGenerics(names) } Executando o benchmark foi possível ver o resultado:
eminetto@MacBook-Pro-da-Trybe ~/D/post-generics> go1.18rc1 test -bench=. -benchtime=100x goos: darwin goarch: arm64 pkg: github.com/eminetto/post-generics BenchmarkMain-8 100 482.1 ns/op BenchmarkUniq-8 100 1225 ns/op BenchmarkGenericsUniq-8 100 1142 ns/op PASS ok github.com/eminetto/post-generics 0.210s Eu executei várias vezes o benchmark e na maioria a versão feita com Generics foi mais performática, apesar da diferença não ter sido tão grande.
Observações
Este post não é um estudo avançado, com benchmarks cientificamente comprovados, é apenas um teste básico. Então mais fontes devem ser consultadas antes de tomarmos uma decisão final, mas a primeira impressão é que estamos ganhando uma feature importante sem perda perceptível de performance.
Eu acredito que vou esperar a versão final desta funcionalidade estar mais madura, provavelmente depois da 1.18.x, para colocá-la em produção, mas vejo uma grande evolução nas aplicações Go nos próximos meses. A empolgação está começando a aumentar :)


Top comments (0)