Generics in Go Tutorial
Tags: go, tutorial
Published: 2022-08-01 09:07:19

## Introduction

In the Go programming language with the version of 1.18 or later there is a new feature called Generics. Generics are a feature that has been featured in many programming languages like Java. Generics are a feature to define a type or data type as a parameter in a function or a `struct`.

## Using Generics in Function

The generics mechanism can be applied in a function. To use a generics feature, the `any` keyword can be used to define all data types that can be used as a parameter in a function or a `struct`. The other keyword that can be used is `comparable` which means the data type that implements `comparable` like `int` and `float64`.

This is an example of generics usage in a function. Before the generics are used, two functions are created called `compareInt` and `compareFloat` to compare two numbers for different data types including `int` and `float64`.

```go
// compare two numbers for int data type
func compareInt(x, y int) bool {
    return x == y
}

// compare two numbers for float64 data type
func compareFloat(x, y float64) bool {
    return x == y
}

```

This is the example of generics usage in function to compare two numbers.

```go
func compare[T comparable](x, y T) bool {
    return x == y
}

```

Based on the code above, the generics are implemented in `[T comparable]` syntax. The `T` is a variable name for the generic parameter. The variable name for the generic parameter can contain many characters like `numType` but the parameter name in general only contains a single character. The `comparable` syntax means a function can be used by a data type that implements `comparable` like `int` and `float64`.

This is an example of a function call that implements the generics.

```go
package main

import "fmt"

func main() {
    fmt.Println("compare result for int: ", compare(2, 3))
    fmt.Println("compare result for float64: ", compare(2.9, 2.9))
}

```

Output.

```
compare result for int:  false
compare result for float64:  true
```

The generics can be used to define the required data types for the function with the `|` operator. This is the example of generics usage in the `average()` function.

```go
// calculate the average
// T is an int or float64 data type
func average[T int | float64](nums []T) T {
    var sum T = 0

    for _, num := range nums {
        sum += num
    }

    var result T = sum / T(len(nums))

    return result
}

```

Based on the code above, the usage of the generic can be seen in `[T int | float64]` syntax which means the function only can be used by the `int` or `float64` data type.

This is the example of the `average()` function call.

```go
package main

import "fmt"

func main() {
    var intData []int = []int{1, 2, 3, 4}
    var floatData []float64 = []float64{2.5, 3.4, 6.7, 2.9}

    fmt.Println("average result for int data type: ", average(intData))
    fmt.Println("average result for float64 data type:", average(floatData))
}

```

Output.

```
average result for int data type:  2
average result for float64 data type: 3.8750000000000004
```

## Using Generics in Struct

The generics can be applied in a `struct`. The generics usage is similar in a function by using certain keywords like `any`, and `comparable` and defining the required data types with the `|` operator.

This is the example of a `struct` without generics implementation.

```go
type IntBox struct {
    Content int
}

type StringBox struct {
    Content string
}

```

This is the example of generics usage in a `struct` called `Box`.

```go
type Box[T any] struct {
    Content T
}

```

Based on the code above, the usage of the generic can be seen in `[T any]` syntax which means the `struct` can be used with any data type.

This is an example of `struct` usage that implements generics.

```go
package main

import "fmt"

type Box[T any] struct {
    Content T
}

func main() {
    var numberBox Box[int] = Box[int]{
        Content: 200,
    }

    var stringBox Box[string] = Box[string]{
        Content: "my content",
    }

    fmt.Println(numberBox.Content)
    fmt.Println(stringBox.Content)
}

```

Output.

```
200
my content
```

This is another example of generics usage in a `struct` with two generic parameters.

```go
type Pair[K, V any] struct {
    Key   K
    Value V
}

```

> The number of the generic parameter is not restricted. In general, the number of the generic parameter is one or two parameters.

This is an example of `struct` usage that used two generic parameters.

```go
package main

import "fmt"

type Pair[K, V any] struct {
    Key   K
    Value V
}

func main() {
    var pairOne Pair[string, int] = Pair[string, int]{
        Key:   "score",
        Value: 2000,
    }

    var pairTwo Pair[int, string] = Pair[int, string]{
        Key:   10,
        Value: "a value",
    }

    fmt.Println(pairOne.Key, pairOne.Value)
    fmt.Println(pairTwo.Key, pairTwo.Value)
}

```

Output

```
score 2000
10 a value
```

## Using Generics in Interface

The generics can be used with the interface. This is an example of generics usage in an interface.

```go
// Number is an int or float64 data type
type Number interface {
    int | float64
}

```

Based on the code above, the interface called `Number` indicates a certain data type that has an `int` or `float64` data type.

The interface that uses the generics can be applied in a function or a `struct`. This is an example of generic interface usage in a function.

```go
// calculate average
// using Number interface as a type
func average[T Number](nums []T) T {
    var sum T = 0

    for _, num := range nums {
        sum += num
    }

    var result T = sum / T(len(nums))

    return result
}

```

Based on the code above, the `Number` interface is used inside the `average()` function which means the `average` function can be called or used by a data type that implements the `Number` interface including `int` or `float64` data type. With the interface, the code is more readable and well structured.

## Notes

- The generics can be used to reduce code duplication if the code has a similar logic structure. For example, a single generic function to calculate an average can be created instead of creating many average functions for many data types.

```go
// without generics, each average function is created for each data type
func averageInt(nums []int) int {
    var sum int = 0

    for _, num := range nums {
        sum += num
    }

    var result int = sum / len(nums)

    return result
}

func averageFloat(nums []float64) float64 {
    var sum float64 = 0

    for _, num := range nums {
        sum += num
    }

    var result float64 = sum / float64(len(nums))

    return result
}

```

```go
// with generics, the average function can be used for int and float64 data type
func average[T int | float64](nums []T) T {
    var sum T = 0

    for _, num := range nums {
        sum += num
    }

    var result T = sum / T(len(nums))

    return result
}

```

## Sources

- [Generics usage in Go programming language](https://go.dev/doc/tutorial/generics).

I hope this article is helpful to learn generics in Go. If you have any thoughts, you can write it in the discussion section below.