← Back to Go Course | Chapter 12: Generics | Lesson 5 of 6

Type Constraints

A constraint is a rule that says 'you can use any type here, as long as it can do these specific things', like requiring every recipe ingredient to at least be edible.

Defining a Constraint Interface

A type constraint is written as an interface, and a generic function's type parameter references it to limit which concrete types are legal -- restricting the operations available inside the function to what the constraint guarantees.

Example: Defining a Constraint Interface

markup
package main

import "fmt"

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

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

func main() {
	fmt.Println(Max(3, 9))
	fmt.Println(Max("apple", "banana"))
}

Constraints with Method Requirements

Like a normal interface, a constraint can require specific methods rather than (or in addition to) a union of allowed types, letting generic code call those methods on any value of the type parameter.

Example: Constraints with Method Requirements

markup
package main

import "fmt"

type Stringer interface {
	String() string
}

func PrintAll[T Stringer](items []T) {
	for _, item := range items {
		fmt.Println(item.String())
	}
}

type Item struct {
	Name string
}

func (i Item) String() string { return "Item: " + i.Name }

func main() {
	PrintAll([]Item{{Name: "Pen"}, {Name: "Book"}})
}

Combining Unions and Methods

A constraint interface can combine a type union with method requirements, giving fine-grained control over exactly which types are acceptable and what operations the generic code may perform on them.

Example: Combining Unions and Methods

markup
package main

import "fmt"

type Numeric interface {
	~int | ~float64
}

func Average[T Numeric](nums []T) float64 {
	var total T
	for _, n := range nums {
		total += n
	}
	return float64(total) / float64(len(nums))
}

func main() {
	fmt.Println(Average([]int{2, 4, 6}))
}
Common Mistakes
  1. Defining a constraint interface but forgetting a union of types (int | float64) is what actually allows arithmetic operators, not method requirements alone.
  2. Trying to use a struct's own methods as an implicit constraint without explicitly declaring an interface listing them.
  3. Overcomplicating a constraint when the built-in any or the standard library's constraints/cmp package already covers the need.
Chapter Summary
  • A constraint is an interface that limits which types can be substituted for a type parameter.
  • A union of types, like int | float64, is used in a constraint to allow operators such as + or <.
  • The built-in comparable constraint allows == and != on a type parameter.
  • Constraints can also require specific methods, just like any regular interface.
🔒

Chapter Quiz — Complete all 6 topics to unlock

0/6 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.