Unlock Go 1.18 Generics: A Complete Guide with Real‑World Code

This article provides a thorough, step‑by‑step exploration of Go 1.18 generics, explaining what generics are, their advantages and drawbacks, how Go implements them compared with other languages, and offering detailed examples of generic variables, functions, methods, interfaces, type constraints, and best‑practice patterns for developers.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
Unlock Go 1.18 Generics: A Complete Guide with Real‑World Code

Introduction

Go 1.18 introduced official support for generics, allowing developers to write type‑safe, reusable code without sacrificing performance.

What are Generics?

Generics let you define a type parameter (e.g., T) that can be substituted with concrete types such as int, float64, or string. The substitution is constrained by a type set that limits the allowed concrete types.

Built‑in Constraints

any

– an alias for interface{}, matches any type. comparable – matches all types that support == and !=. constraints.Ordered (in golang.org/x/exp/constraints) – matches types that support ordering operators ( <, <=, >=, >).

Defining Generic Types

Generic slice type Slice1[T int|float64|string] []T Instantiation requires an explicit type argument:

var intSlice Slice1[int] = []int{1, 2, 3}
var strSlice Slice1[string] = []string{"a", "b"}

Generic map

type Map1[KEY int|string, VALUE string|float64] map[KEY]VALUE
var m Map1[int]string = map[int]string{1:"one"}
var m2 Map1[string]float64 = map[string]float64{"pi":3.14}

Generic struct

type Struct1[T string|int|float64] struct {
    Title   string
    Content T
}
var s Struct1[int] = Struct1[int]{Title:"Num", Content:42}

Nested generic types

type MyStruct[S int|string, P map[S]string] struct {
    Name    string
    Content S
    Job     P
}
var ms MyStruct[int, map[int]string] = MyStruct[int, map[int]string]{
    Name:"demo", Content:1, Job:map[int]string{1:"val"}}

Generic Functions

Function type parameters are declared after the function name inside square brackets.

func Sum[T int|float64](a, b T) T {
    return a + b
}

Calls can specify the type explicitly or rely on type inference:

intResult := Sum[int](1, 2)
floatResult := Sum(1.5, 2.3) // compiler infers T = float64

Custom Constraints

Complex type sets can be factored into reusable constraints.

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

func Foreach[T MyNumber](list []T) {
    for _, v := range list {
        fmt.Println(v)
    }
}

Constraints can be composed:

type myInt interface { int | int8 | int16 | int32 | int64 }
type myUint interface { uint | uint8 | uint16 | uint32 }
type myFloat interface { float32 | float64 }
type myNumber interface { myInt | myUint | myFloat }

Using constraints.Ordered and comparable

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

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

func Match[T comparable](x, y T) bool { return x == y }

Generic Methods (Receiver Generics)

A method can have a generic receiver type.

type NumberAll interface { ~int | ~float64 }

type SliceNumber[T NumberAll] []T

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

intVals := SliceNumber[int]{1,2,3}
fmt.Println(intVals.Sum()) // 6

Example: a generic stack.

type Number interface { int | int32 | int64 | float32 | float64 }

type Stack[V Number] struct {
    size  int
    value []V
}

func (s *Stack[V]) Push(v V) { s.value = append(s.value, v); s.size++ }
func (s *Stack[V]) Pop() V {
    e := s.value[s.size-1]
    if s.size > 0 { s.value = s.value[:s.size-1]; s.size-- }
    return e
}

s := &Stack[int]{}
s.Push(1); s.Push(2); fmt.Println(s.Pop()) // 2

Generic Interfaces

Two kinds of interfaces exist after Go 1.18.

Basic generic interface : only methods, no type set. Example:

type Writer[T int|string] interface {
    WriteOne(data T) T
    ReadOne() T
}

type Note struct{}
func (n Note) WriteOne(data string) string { return "hello" }
func (n Note) ReadOne() string { return "world" }

var w Writer[string] = Note{}
fmt.Println(w.WriteOne("test"))

General generic interface : contains a type set (e.g., int | string) and may also declare methods. Such interfaces can only be used as type parameters, not instantiated directly.

Special Syntax

The tilde ( ~) denotes a type approximation: ~int matches int and any defined type whose underlying type is int. This is useful when a constraint must accept user‑defined types based on a primitive.

type IntAll interface { ~int | ~int64 }

type MySlice[T IntAll] []T

type MyInt int // underlying type is int
var s MySlice[MyInt] // valid because MyInt satisfies ~int

Common Pitfalls

When instantiating a generic type, the type argument list must be supplied (e.g., Slice1[int]). Omitting it results in “undefined type parameter”.

Anonymous generic structs are not supported; the compiler expects a concrete type after the struct keyword.

A constraint that resolves to an empty type set (e.g., int | string when used as a constraint without any concrete type) causes compile‑time errors because no type can satisfy it.

Using any as a constraint for arithmetic operations fails because the compiler cannot guarantee that the + operator is defined for all possible types.

Summary

Go’s generic system provides:

Type parameters with explicit type sets (constraints) to restrict admissible concrete types.

Built‑in constraints ( any, comparable, constraints.Ordered) for common use‑cases.

Ability to define generic types (slices, maps, structs), functions, methods, and interfaces.

Composition of constraints via interfaces to keep code readable and maintainable.

Type inference that lets calls omit explicit type arguments when the compiler can deduce them.

By leveraging these features, developers can eliminate duplicated code while preserving compile‑time type safety and runtime performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend DevelopmentprogrammingGoGenericsGo 1.18Type ConstraintsGeneric Functions
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.