Começando com generics em Go
Tags: go, generics, programming, braziliandevs
Published: 2023-12-27 10:12:29

---
title: Começando com generics em Go
published: true
description: Um pequeno experimento com linguagem Go (Golang), para testar generics
tags: go,generics,programming,braziliandevs
# cover_image: ![Imagem criada no Bing Image Creator com o prompt imagem de marmota gorda de óculos programadora com camiseta da linguagem go](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uwwpw78i5jzapotktrig.jpg)
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-27 09:53 +0000
---

## Motivação

Estava estudando algoritmos de ordenação, e implementando em Go. Achei chato fazer um código que só trabalhasse com inteiros, lembrando que _Golang_ tem _generics_.A partir disso, muitas leituras, tentativas, até o fracasso. Implementar no algoritmo foi fácil, o problema quase incontornável foi nos testes, já que insisti em seguir o "table driven tests", para variados tipos em uma só função de teste.

Retomando o desafio, pensei em uma forma mais simples de testar a mesma estrutura, para não ficar perdendo tempo com erros e detalhes diferentes do novo objetivo de aprender _generics_.

Aqui segue o resultado desse experimento.

## A tarefa

Nessa _subtask_, defini que trabalharia em uma função mais simples, que mantivesse o espírito do problema, mas que me permitisse estudar principalmente a forma de testar.

Resolvi implementar o que chamo "método de ordenação if". Dados dois números, a função deve retorná-los ordenados (`ifsort.go`):

```
package ifsort

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

func Sort[T constraints.Ordered](a, b T) (T, T) {
    if a > b {
        return b, a
    }

    return a, b
}

```

Poderíamos ter várias funções, cada qual com um tipo diferente, mas o corpo da função seria o mesmo. Essa é a uma das belezas dos generis: reutilização de código, evitando que depois do "copia e cola", algo fique diferente em alguma dessas funções clonadas.

Poderia, por exemplo, usar as primitivas da linguagem para definir um tipo _interface_, como em:

```
type MyGenericType interface {
    int | float64 | string
}
```

Mas já que temos algo pronto :), bora lá, está completo e com mais possibilidades. O Ordered é definido como:

```
type Ordered interface {
    Integer | Float | ~string
}
```

Note que _Integer_ e _Float_ expandem para todas suas variantes. E o operador `~` usado no tipo `string`, significa que construções como `MyStringType` serão contempladas. O operador `|` indica a união de todas essas possibilidades.

O mais importante, com esse tipo: garantimos que nossa função só trabalha com tipos que possam ser comparados; nesse caso concreto, precisamos garantir que tenham implementado o operador `>`.

O pacote constraints nos traz vários tipos, que garantem certas propriedades:

    https://pkg.go.dev/golang.org/x/exp/constraints#section-documentation

Uma boa introdução sobre generics pode ser encontrada no próprio site da linguagem:

    https://go.dev/blog/intro-generics

## Testando

Óbvio que numa situação real, não precisamos testar cada tipo para cobrir o espectro do _generics_ que estivermos usando. Mas, como se tratava de um experimento, prefiro sempre escrever testes do que criar uma função main e ficar alterando e testando manualmente.

Eis o arquivo de teste (`ifsort_test.go`):

```
package ifsort

import (
    "testing"

    "github.com/stretchr/testify/assert"
    "golang.org/x/exp/constraints"
)

func runAssertEqual[T constraints.Ordered](t *testing.T, input []T, output []T) {
    a, b := Sort(input[0], input[1])
    assert.Equal(t, a, output[0])
    assert.Equal(t, b, output[1])
}

type TestIntType struct {
    input  []int
    output []int
}

func TestBubbleInt(t *testing.T) {
    for _, test := range []TestIntType{
        {[]int{1, 2}, []int{1, 2}},
        {[]int{50, -101}, []int{-101, 50}},
    } {
        runAssertEqual(t, test.input, test.output)
    }
}

func TestBubbleFloat64(t *testing.T) {
    runAssertEqual(t, []float64{22.2, 11.1}, []float64{11.1, 22.2})
}

func TestBubbleString(t *testing.T) {
    runAssertEqual(t, []string{"z", "a"}, []string{"a", "z"})
}
```

Não é uma prática recomendada, reutilização de código em um teste, e nem eu gosto muito disso, mas para o objetivo aqui, tratava-se de mais uma oportunidade para brincar com _generics_:

```

func runAssertEqual[T constraints.Ordered](t *testing.T, input []T, output []T) {
    a, b := Sort(input[0], input[1])
    assert.Equal(t, a, output[0])
    assert.Equal(t, b, output[1])
}
```

Mais uma vez, independente do tipo, chamamos a função de ordenação, e então verificamos o resultado. E para facilitar a leitura do código, usa-se o pacote `testify`:

    https://github.com/stretchr/testify

Usando essa abordagem, facilita-se a execução dos testes específicos. Tarefa para o fim de ano, voltar ao problema original dos algoritmos de ordenação, que requerem uma abordagem levemente diferente, por conta dos _slices_, que serão usados, no lugar de parâmetros individuais.

Publicado originalmente em https://paulohrpinheiro.xyz/texts/go/2023-12-22-comecando-com-generics-em-go.html