✨ How will Go Generics in Go 1.18 change your life? 💜
Tags: go, programming, news, beginners
Published: 2022-03-09 17:20:37

With the arrival of a long awaited update for some, or an update like the others for others. Generics in Go are about to be your new code companion in your Go projects.

# A concrete example? Yes

For me one of the easiest ways to understand a new feature is to have an example. Until 1.17 (without Generics) having a function that created a slice of an element was easy for a `type` but if you wanted to manage several types it was immediately less fun. 

```go
// 1.17 without generic
func JoinBool(elements ...bool) []bool {
  return elements
}

func JoinInt(elements ...int) []int {
  return elements
}

func JoinString(elements ...string) []string {
  return elements
}

func main() {
  slice := JoinInt(1, 2, 3)
  slice2 := JoinBool(true, true, false)
  slice3 := JoinString("a", "b", "c")
}
```
In 1.17, you had to create a function for each `type` and call it individually. 

**🎉 A new foe has appeared : Go Generic**

```go
// 1.18 with generics
func Join[E any](elements ...E) []E {
  return elements
}
 
func main() {
  slice := Join[int](1, 2, 3)
  slice2 := Join[bool](true, true, false)
  slice3 := Join[string]("a", "b", "c")
}
```
In 1.18 the generics allow you to program your types. Yes, I think it's sexy to say it like that!

What am I reading? From Go!
everything between `[]` is the definition of your types. 

In the definition of the function `func Join[E any](elements ...E) []E` as well as in the call `slice := Join[int](1, 2, 3)`.
The `type` is defined when the function is called and used in the function code.


Let's split the function `Join` with the call `Join[int](1, 2, 3)`
```go
func Join[E any](elements ...E) []E {
  return elements
}
```

In the example above I create an `E` type which can be of type `any` (keyword to say that everything is accepted). If you want a hint of understanding, imagine that at runtime you replace all `E` by `int` in a dynamic way. 

Your function will then run like this if I simplify
```go
// E type become int
func Join(elements ...int) []int {
  return elements
}
```
Now I let you imagine with all the other guys
Do you see it? Do you feel the power of Generics? 

# `any` is the new `interface{}` ?

Yes, but... No. Until now, if you wanted a function to accept several types, you used the `interface{}` hack which allowed you to receive anything. However it was to be used with care! And a type check was mandatory. 

Let's take our `Join` function coded with `interface{}` again

```go
func Join(elements ...interface{}) ([]interface{}, error) {
  // Check the interface type
  switch t := elements.(type) {
    case []int, []string:
      return elements, nil
    default:
      return nil, errors.New("Unsupported type")
  }
}

func main() {
  slice, err := Join(1, 2, 3)
  if err != nil {
    panic(err)
  }
  // slice is now a type of `[]interface{}`
  // Not a good idea. A Disaster ! To convert to []int again we needs to apply 
  // a second for loop
  var intSlice = []int{}
  for _, v := range slice {
    // Cast and check if the type of the interface{} is castable to `int` type
    if intCasted, ok := v.(int); ok {
      intSlice := append(intSlice, intCasted)
    }
  }

  // intSlice is not usable after a lot of pain...
}
```

When you use `interface{}`, it is mandatory to test your cast. Otherwise it's a crash! The cast is done **at runtime** which is quite risky for your programs.

**🎉 A new foe has appeared : Go Generic**
Here they come again! Version 1.18 is much nicer to read now that we know that our types are defined and checked at **buildtime**. If you use an IDE like VSCode, it will even warn you before building.

# `any` everywhere !

**NO! Never assume to set `any` by default!** You should only start with the desired types, and as a last resort use `any`.

You will say yes, but Atomys, how can I do if my function can receive several types? The answer is simple, you can list as many types as you want:

```go
func JoinNumeric[E int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64](elemets ...E) []E
```
My function accepts here a generic type `E` which could ONLY be of type numeric.

Yikes, but I'll have to write this every time? No, don't worry!

It is possible to create your own type, using `interfaces`. And yes, they are not going to disappear.

Let's create our `Numeric` type!

```go
type Numeric interface {
    int | int8 | int16 | int32 | int64 | uint | uint8 | uint16 | uint32 | uint64 | float32 | float64
}
```

And now let's update our `Join` function declaration
```go
func JoinNumeric[E Numeric](elemets ...E) []E
``` 
WOW! So much cleaner. Yes I'll give you that.


This is a concrete example of what Go Generics can bring in 1.18. But that's not all two new features `comparable` and the `constraints` package. We will see that in a next article ! 

Thanks for taking the time to read my article, it was my first one ! 

🙏 Please don't hesitate to support me with a follow on [Github/42Atomys](https://github.com/42Atomys) and [DEV.to/42Atomys](https://dev.to/42atomys). New Go projects are coming soon.

💜💜