Use type parameters for generic functions and data structures in Go
Starting with version 1.18, Go added support for generics (also known as type parameters). This allows you to write functions and data structures that work with any type while maintaining type safety.
package mainimport "fmt"// SlicesIndex takes a slice of any comparable type and an element,// returning the index of the first occurrence of v in s, or -1 if not present.func SlicesIndex[S ~[]E, E comparable](s S, v E) int { for i := range s { if v == s[i] { return i } } return -1}func main() { var s = []string{"foo", "bar", "zoo"} // When invoking generic functions, we can often rely on type inference. // We don't have to specify the types for S and E - the compiler infers them. fmt.Println("index of zoo:", SlicesIndex(s, "zoo")) // Though we could also specify them explicitly. _ = SlicesIndex[[]string, string](s, "zoo")}
Output:
index of zoo: 2
This SlicesIndex function is similar to the standard library’s slices.Index function.
func SlicesIndex[S ~[]E, E comparable](s S, v E) int
Let’s break down this signature:
[S ~[]E, E comparable]
Type parameter list. Defines two type parameters:
S - constrained to slice types (~[]E)
E - constrained to comparable types
~[]E
The ~ means “underlying type”. S can be any type whose underlying type is []E, allowing custom slice types.
comparable
A built-in constraint meaning the type can be compared with == and !=. Includes booleans, numbers, strings, pointers, channels, and arrays/structs of comparable types.
For a thorough explanation of type parameter syntax, see this blog post.
// List is a singly-linked list with values of any type.type List[T any] struct { head, tail *element[T]}type element[T any] struct { next *element[T] val T}// Methods on generic types keep the type parameters.// The type is List[T], not List.func (lst *List[T]) Push(v T) { if lst.tail == nil { lst.head = &element[T]{val: v} lst.tail = lst.head } else { lst.tail.next = &element[T]{val: v} lst.tail = lst.tail.next }}func (lst *List[T]) AllElements() []T { var elems []T for e := lst.head; e != nil; e = e.next { elems = append(elems, e.val) } return elems}func main() { lst := List[int]{} lst.Push(10) lst.Push(13) lst.Push(23) fmt.Println("list:", lst.AllElements())}