Generics
Generics enables you to write functions and data structures that work with different types while maintaining type safety
. You can define functions and structures that operate on various types without sacrificing the compiler's ability to catch type errors at compile time.
Example
package main
import (
"fmt"
)
func FindMax[T int | float64](data []T) T {
if len(data) == 0 {
return 0
}
max := data[0]
for _, value := range data {
if value > max {
max = value
}
}
return max
}
func main() {
ints := []int{3, 7, 1, 9, 4, 6}
floats := []float64{3.14, 2.71, 1.618, 2.718}
fmt.Println("FindMax integer:", FindMax(ints))
fmt.Println("FindMax float:", FindMax(floats))
}
Combine Multiple Types (Constraint)
In Go, generic constraints are a way to specify requirements for the type parameters used in generic functions or data structures
. Constraints ensure that the generic code can only be used with types that meet certain criteria
.
Example
package main
import (
"fmt"
)
type ValidNumbers interface {
int | float64
}
func FindMax[T ValidNumbers](data []T) T {
if len(data) == 0 {
return 0
}
max := data[0]
for _, value := range data {
if value > max {
max = value
}
}
return max
}
func main() {
ints := []int{3, 7, 1, 9, 4, 6}
floats := []float64{3.14, 2.71, 1.618, 2.718}
fmt.Println("FindMax integer:", FindMax(ints))
fmt.Println("FindMax float:", FindMax(floats))
}
Tilde(~) - Closer Than Identical Types
The special tilde (~
) symbol, often referred to as the approximation constraint
, is used in Go generics to indicate that a type should approximately match one of the specified types or their derived types
. Without the tilde, the function or method constrained by generic types would only accept exact types declared in the constraints and not their derived types
. The tilde allows for a broader match, including derived types, making your code more flexible and accommodating related types.
Example
package main
import (
"fmt"
)
type NumberInt int
type NumberFloat float64
type ValidNumbers interface {
~int | ~float64
}
func FindMax[T ValidNumbers](data []T) T {
if len(data) == 0 {
return 0
}
max := data[0]
for _, value := range data {
if value > max {
max = value
}
}
return max
}
func main() {
ints := []NumberInt{3, 7, 1, 9, 4, 6}
floats := []NumberFloat{3.14, 2.71, 1.618, 2.718}
fmt.Println("FindMax integer:", FindMax(ints))
fmt.Println("FindMax float:", FindMax(floats))
}
Comparable
In Go generics, you can use the comparable constraint
to specify that a type should be comparable. This constraint ensures that the type can be used with comparison operators like ==
and !=
.
Example
package main
import "fmt"
func CheckNumbers[T comparable](a T, b T) bool {
if a == b {
return true
}
return false
}
func FindIndex[T comparable](data []T, value T) int {
for i, data_value := range data {
if data_value == value {
return i
}
}
return -1
}
func main() {
fmt.Println("CheckNumbers: ", CheckNumbers(10, 10.5))
fmt.Println("CheckNumbers: ", CheckNumbers(10.5, 10.5))
ints := []int{3, 7, 1, 9, 4, 6}
fmt.Println("FindMax integer:", FindIndex(ints, 300))
fmt.Println("FindMax integer:", FindIndex(ints, 9))
fmt.Println("FindMax integer:", FindIndex(ints, 10))
}