Menu
Coddy logo textTech

Introduction to Generics

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 67 of 107.

Before Go 1.18, writing reusable code for different types meant either duplicating functions or using the empty interface any with type assertions. Generics solve this problem by letting you write functions and types that work with multiple types while keeping full type safety.

Consider finding the minimum value in a slice. Without generics, you'd need separate functions for each type:

func MinInt(values []int) int {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

func MinFloat64(values []float64) float64 {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

With generics, you write the logic once using a type parameter in square brackets:

func Min[T int | float64](values []T) T {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

The [T int | float64] declares a type parameter T that can be either int or float64. Now you can call Min([]int{3, 1, 2}) or Min([]float64{3.5, 1.2}) with the same function. The compiler checks types at compile time, so you get safety without runtime overhead.

In the upcoming lessons, we'll explore type parameters, constraints, and generic structs in detail.

challenge icon

Challenge

Easy

Let's build a score analyzer that works with both integer and floating-point scores using generics! You'll create a single generic function that can find the maximum value in a slice, eliminating the need for duplicate code for different numeric types.

You'll organize your code across two files:

  • analyzer.go: Define your generic analysis function.

    Create a generic function Max[T int | float64](values []T) T that finds and returns the maximum value in a slice. The function should work with both int and float64 types through the type parameter T.

    Also create a generic function Sum[T int | float64](values []T) T that calculates and returns the sum of all values in a slice.

  • main.go: Read input and demonstrate your generic functions with different types.

    Read a type indicator (int or float), then read a count followed by that many values. Parse the values according to the type, call both Max and Sum with the appropriate type, and print the results.

    For integer input, print:

    Max (int): [value]
    Sum (int): [value]

    For float input, print with one decimal place:

    Max (float): [value]
    Sum (float): [value]

The following inputs will be provided:

  • Line 1: Type indicator (int or float)
  • Line 2: Count of values (integer)
  • Following lines: One value per line

For example, given:

int
4
15
8
23
11

Your output should be:

Max (int): 23
Sum (int): 57

And given:

float
3
4.5
9.2
6.8

Your output should be:

Max (float): 9.2
Sum (float): 20.5

Notice how the same generic functions handle both integer and floating-point data—the type parameter [T int | float64] allows the compiler to generate type-safe code for each usage while you only write the logic once.

Cheat sheet

Generics allow you to write reusable code that works with multiple types while maintaining type safety. Before Go 1.18, you needed separate functions for each type or used the empty interface with type assertions.

Type parameters are declared in square brackets and specify which types a generic function can accept:

func Min[T int | float64](values []T) T {
    min := values[0]
    for _, v := range values {
        if v < min {
            min = v
        }
    }
    return min
}

The [T int | float64] syntax declares a type parameter T that can be either int or float64. The pipe | operator lists the allowed types.

You can call generic functions with different types:

Min([]int{3, 1, 2})           // Works with int
Min([]float64{3.5, 1.2})      // Works with float64

The compiler performs type checking at compile time, providing safety without runtime overhead.

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Read type indicator
	typeStr, _ := reader.ReadString('\n')
	typeStr = strings.TrimSpace(typeStr)

	// Read count
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))

	if typeStr == "int" {
		// Read integer values
		values := make([]int, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.Atoi(strings.TrimSpace(line))
		}

		// TODO: Call Max and Sum with integer slice
		// TODO: Print results in format "Max (int): [value]" and "Sum (int): [value]"

	} else if typeStr == "float" {
		// Read float values
		values := make([]float64, count)
		for i := 0; i < count; i++ {
			line, _ := reader.ReadString('\n')
			values[i], _ = strconv.ParseFloat(strings.TrimSpace(line), 64)
		}

		// TODO: Call Max and Sum with float64 slice
		// TODO: Print results with one decimal place in format "Max (float): [value]" and "Sum (float): [value]"
	}
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming