// syntax
func name[T contraint1, U constraint2 | constraint3](a T, b U) T {
	// defines generics T that must implement constraint1
	// and U which must implement either constraint2 or constraint3
	// returns type T
}
 
// example
func IsEqual[T comparable](a, b T) bool {
	return a == b
}
 
func main() {
	IsEqual(1, 2) // false
	IsEqual("a", "a") // true
	IsEqual[uint8](4, 4) // true (uses unsigned integers)
}

Creating a constraint

// defines a constraint
type Integers32 interface {
	int32 | uint32
}
 
type MyInt uint32
 
// works for either unit32 or int32, NOT MyInt
func Sum[T Integers32](arr []T) T {
	var sum T
	for _, num := range arr {
		sum += num
	}
	return sum
}
 
// approximation
type ApproxIntegers32 interface {
	~int32 | ~uint32
}
 
// works for MyInt, and other types that are at base either int32 or uint32
func Sum[T ApproxIntegers32](arr []T) T {
	var sum T
	for _, num := range arr {
		sum += num
	}
	return sum
}

Generic structures

 
type MyArr[T constraints.Ordered] struct {
	arr []T
}
 
func (a *MyArr[T]) max() T {
	if len(a.arr) < 1 {
		panic("Must have an non-empty array")
	}
	var max T = a.arr[0]
	for _, val := range a.arr {
		if max < val {
			max = val	
		}
	}
	return max
}
 
func main() {
	myarr := MyArr[int]{[]int{1, 2, 3, 4}}
	myarr.max() // returns 4
}