# Generics

Course: [Go](https://www.devtutorials.ai/learn/go) · Section: Types and OOP
Source: https://www.devtutorials.ai/learn/go/types-and-oop/generics

> Go generics: type parameters, generic functions and return types, generic structs, the any and comparable constraints, custom constraints, and practical generic helpers.

## What Are Generics?

**Generics** = writing a function or a type **once**, so it works with many types, while Go still checks every type.

Without generics, you might write:

- `SumInts(list []int) int`
- `SumFloats(list []float64) float64`
- the same code twice, with only the type changed

With generics, you write one `Sum[T Number](list []T) T`.

**Simple lesson:** generics let you write one piece of code for many types, safely.

### A Real-World Example

Think of a stencil for the school notice board.

- One **stencil** has the shape of a star.
- You can paint it in **red**, **blue** or **gold**.
- The shape is the same every time. Only the colour changes.
- But the stencil only takes paint, not glue or sand.

Now the mapping:

- **The stencil** = the generic function.
- **The colour you choose** = the type parameter, like `int` or `string`.
- **"Paint only"** = the constraint: which types are allowed.

Generics were added in **Go 1.18** (2022).

Older Go code often uses `any` and type assertions instead. Generics are safer, because Go checks the types before running. See **Interfaces and Polymorphism**.

## What You Will Learn

In this lesson, you will learn:

1. What generics and type parameters are.
2. How generics give reusable code with type safety.
3. How to write generic functions with generic parameters and return types.
4. How to write generic structs.
5. The constraints `any`, `comparable` and your own constraints.
6. Practical examples: containers, utility functions and reusable algorithms.

## Generic Fundamentals

### Type Parameters

**Type parameter** = a placeholder for a type, written in square brackets: `[T any]`.

```go
func First[T any](list []T) T {
	return list[0]
}
```

- `[T any]` = "T stands for some type. Any type is allowed."
- Inside the function, `T` is used like a normal type.
- When you call `First([]string{"a"})`, Go fills in `T = string`.

### Reusable Code

Pick a type, and see what Go builds from the same code.

- The code is written **once**.
- Go checks it again for each type you use.

### Type Safety

**Type safety** = Go checks that every value has the right type before the program runs.

**Simple lesson:** a type parameter like `[T any]` is a placeholder that Go fills in and checks for each call.

## Generic Functions

### Generic Parameters

```go runnable title="One function, three types"
package main

import "fmt"

func Last[T any](list []T) T {
	return list[len(list)-1]
}

func main() {
	fmt.Println(Last([]int{91, 78, 85}))
	fmt.Println(Last([]string{"Mon", "Tue", "Wed"}))
	fmt.Println(Last([]bool{true, false}))
}
```

```text
85
Wed
false
```

- Go works out `T` from the argument. This is **type inference**. See **Data Types**.
- You can also write it yourself: `Last[int](marks)`.

### Generic Return Types

The return type can use the type parameter too.

```go runnable title="Return a new slice of the same type"
package main

import "fmt"

func Reverse[T any](list []T) []T {
	out := make([]T, len(list))
	for i, v := range list {
		out[len(list)-1-i] = v
	}
	return out
}

func main() {
	fmt.Println(Reverse([]int{1, 2, 3}))
	fmt.Println(Reverse([]string{"A", "B", "C"}))
}
```

```text
[3 2 1]
[C B A]
```

### More Than One Type Parameter

```go runnable title="Map: change every element"
package main

import (
	"fmt"
	"strings"
)

func Map[T, U any](list []T, f func(T) U) []U {
	out := make([]U, 0, len(list))
	for _, v := range list {
		out = append(out, f(v))
	}
	return out
}

func main() {
	names := []string{"asha", "ravi"}
	upper := Map[string, string](names, strings.ToUpper)
	lengths := Map[string, int](names, func(s string) int { return len(s) })
	fmt.Println(upper, lengths)
}
```

```text
[ASHA RAVI] [4 4]
```

- `[T, U any]` has two placeholders: the input type and the output type.
- Here the type arguments are written out: `Map[string, int]`. This is always allowed, and it makes the code clear.

**Simple lesson:** put type parameters in `[ ]` after the function name, and use them for parameters and results.

## Generic Types

### Generic Structs

**Generic type** = a struct (or other type) with type parameters.

```go runnable title="A stack that holds any one type"
package main

import "fmt"

type Stack[T any] struct {
	items []T
}

func (s *Stack[T]) Push(v T) {
	s.items = append(s.items, v)
}

func (s *Stack[T]) Pop() (T, bool) {
	var zero T
	if len(s.items) == 0 {
		return zero, false
	}
	top := s.items[len(s.items)-1]
	s.items = s.items[:len(s.items)-1]
	return top, true
}

func main() {
	books := &Stack[string]{}
	books.Push("Maths")
	books.Push("Science")
	top, ok := books.Pop()
	fmt.Println(top, ok)

	plates := &Stack[int]{}
	_, ok = plates.Pop()
	fmt.Println(ok)
}
```

```text
Science true
false
```

- `Stack[string]` and `Stack[int]` are two different types made from one definition.
- Methods write the receiver as `(s *Stack[T])`.
- `var zero T` gives the zero value of whatever `T` is.

Press **Play** to watch the stack.

**Simple lesson:** a generic struct like `Stack[T]` gives you one container design for every type.

## Constraints

**Constraint** = the rule for which types a type parameter accepts.

### any

- `[T any]` = every type is allowed.
- But you can only do things that work for **every** type: store it, return it, pass it on.
- You cannot use `+` or `<` on it.

### comparable

**comparable** = types that work with `==` and `!=`.

```go runnable title="Count matches with comparable"
package main

import "fmt"

func Count[T comparable](list []T, target T) int {
	n := 0
	for _, v := range list {
		if v == target {
			n++
		}
	}
	return n
}

func main() {
	votes := []string{"Asha", "Ravi", "Asha", "Asha"}
	dice := []int{6, 2, 6, 1}
	fmt.Println(Count(votes, "Asha"), Count(dice, 6))
}
```

```text
3 2
```

- `==` needs `comparable`. With `any`, Go would refuse the `==`.
- Map keys must be comparable too. See **Maps**.

### Custom Constraints

A constraint is an interface. It can list **types** with `|`:

```go runnable title="Sum numbers of any kind"
package main

import "fmt"

type Number interface {
	~int | ~int64 | ~float64
}

func Sum[T Number](list []T) T {
	var total T
	for _, v := range list {
		total += v
	}
	return total
}

type Rupees int

func main() {
	fmt.Println(Sum([]int{10, 20, 30}))
	fmt.Println(Sum([]float64{1.5, 2.25}))
	fmt.Println(Sum([]Rupees{100, 250}))
}
```

```text
60
3.75
350
```

- `int | int64 | float64` = "T must be one of these".
- `~int` = "int, or any type built on int", like `Rupees`. See **Advanced Type Design**.
- Because every allowed type supports `+`, Go lets you write `total += v`.

A constraint can also ask for **methods**:

```go runnable title="A constraint with a method"
package main

import "fmt"

type Labeler interface {
	Label() string
}

type Book struct{ Title string }

func (b Book) Label() string { return "Book: " + b.Title }

func PrintAll[T Labeler](items []T) {
	for _, it := range items {
		fmt.Println(it.Label())
	}
}

func main() {
	PrintAll([]Book{{"Gitanjali"}, {"Malgudi Days"}})
}
```

```text
Book: Gitanjali
Book: Malgudi Days
```

Go 1.21 added the `cmp.Ordered` constraint (all types that work with `<`), and the `slices` and `maps` packages full of generic helpers.

This lab does not run those packages, so the examples write their own constraints. On your computer, prefer the standard ones.

**Simple lesson:** `any` allows everything, `comparable` allows `==`, and your own constraints list types or methods.

## Practical Examples

### Generic Containers

```go runnable title="A Pair that holds two different types"
package main

import "fmt"

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

func main() {
	p1 := Pair[string, int]{"Asha", 91}
	p2 := Pair[int, string]{8, "Class 8"}
	fmt.Println(p1.Key, p1.Value)
	fmt.Println(p2.Key, p2.Value)
}
```

```text
Asha 91
8 Class 8
```

### Generic Utility Functions

```go runnable title="Filter: keep what matches"
package main

import "fmt"

func Filter[T any](list []T, keep func(T) bool) []T {
	var out []T
	for _, v := range list {
		if keep(v) {
			out = append(out, v)
		}
	}
	return out
}

func main() {
	marks := []int{91, 34, 78, 25, 66}
	passed := Filter(marks, func(m int) bool { return m >= 35 })
	fmt.Println(passed)

	names := []string{"Asha", "Ravi", "Aman"}
	aNames := Filter(names, func(s string) bool { return s[0] == 'A' })
	fmt.Println(aNames)
}
```

```text
[91 78 66]
[Asha Aman]
```

### Type-Safe Collections

- `Stack[string]` will only ever hold strings.
- `stack.Push(5)` on a `Stack[string]` is a **build error**, not a runtime surprise.
- That is the main gain over a `[]any` list.

### Reusable Algorithms

```go runnable title="Find the largest, for any ordered type"
package main

import "fmt"

type Ordered interface {
	~int | ~float64 | ~string
}

func Largest[T Ordered](list []T) T {
	best := list[0]
	for _, v := range list[1:] {
		if v > best {
			best = v
		}
	}
	return best
}

func main() {
	fmt.Println(Largest([]int{72, 95, 88}))
	fmt.Println(Largest([]float64{1.5, 9.25, 3.0}))
	fmt.Println(Largest([]string{"kiwi", "mango", "apple"}))
}
```

```text
95
9.25
mango
```

- `>` works because every type in `Ordered` supports it.
- Strings compare in dictionary order, so `"mango"` is largest.

**Easy way to remember**

- Same code, different types → generics.
- Different behaviour per type → interfaces.
- Start with `any`. Tighten the constraint only when you need `==`, `<` or `+`.

**Simple lesson:** use generics for containers and helpers that do the same thing for many types.

## TL;DR

1. **Generics** = write once, use with many types, still type-checked. Added in Go 1.18.
2. **Type parameter** = `[T any]` after the name. Go fills `T` in.
   - **Type inference** = Go works out `T` from the arguments.
   - Or write it: `Map[string, int](...)`.
3. **Generic function** = `func Last[T any](list []T) T`.
4. **Generic struct** = `type Stack[T any] struct { items []T }`.
   - Methods: `func (s *Stack[T]) Push(v T)`.
5. **Constraints**:
   - `any` = all types.
   - `comparable` = types with `==`.
   - **Custom** = `interface { ~int | ~float64 }` or method lists.
   - `~int` = int and types built on int.
6. **Uses** = containers, `Map`, `Filter`, `Sum`, `Largest`.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `a > b` with `[T any]` | Any type can be compared | `>` needs an ordered constraint | `cannot compare a > b (T is not ordered)` |
| `v == x` with `[T any]` | `any` supports `==` | Use `comparable` | `incomparable types in type set` |
| `int | float64` without `~` and passing `Rupees` | Built-on types are included | Add `~` to include them | `Rupees does not satisfy Number` |
| Generics for everything | Generic code is always better | Use them when the code is the same for many types | Hard-to-read code. |
| Using `any` lists instead of generics | They are the same | `any` needs assertions and can panic | Runtime errors. |
| `var zero T = nil` | Every type can be nil | Use `var zero T` | A build error for value types. |

## Check Yourself

## Exercises

## What to Remember

- Generics let one function or type work with many types, still checked by Go.
- Type parameters go in `[ ]`: `[T any]`. Go usually infers them.
- `any` allows all types, `comparable` allows `==`, custom constraints list types or methods.
- `~int` includes types built on `int`.
- Use generics for containers and helpers. Use interfaces when behaviour differs per type.

Next: **Collections of Structs**. You will learn how to keep, search, sort and update many records, like a whole class of students.
