26 October 2022

# Go Generics in Practice

## Map Access (and Set if absent)

``````func GetOrSetDefault[K comparable, V any](m map[K]V, key K, new_ func() V) V {
value, found := m[key]
if !found {
value = new_()
m[key] = value
}
return value
}

func main() {
a := make(map[int]string)
GetOrSetDefault(a, 42, func() string { return "hi" })
fmt.Println(a[42]) // hi
}
``````

## Index a Slice of Objects into a Map

``````func Index[K comparable, V any](list []V, key func(V) K) map[K]V {
result := make(map[K]V)
for _, value := range list {
result[key(value)] = value
}
return result
}

type Thing struct {
Value string
}

func main() {
a := []Thing{{Value: "a"}, {Value: "b"}}
b := Index(a, func(t Thing) string { return t.Value })
fmt.Println(b["a"]) // {a}
}
``````

## Coalesce Function:

``````// coalesce returns the first non-zero-value argument.
// use !reflect.DeepEqual(item, zero) and change T to `any`
// if you want to compare slices.
func coalesce[T comparable](values ...T) (zero T) {
for _, item := range values {
if item != zero {
return item
}
}
return zero
}

func main() {
fmt.Println(coalesce("a", "b")) // "a"
fmt.Println(coalesce("", "b")) // "b"
}
``````

## Load a channel from a slice:

``````func Slice2Channel[T any](slice []T) chan T {
channel := make(chan T)
go func() {
for _, item := range slice {
channel <- item
}
}()
return channel
}
``````

## Drain a channel into a slice:

``````func Channel2Slice[T any] (channel chan T) (slice []T) {
for item := range channel {
slice = append(slice, item)
}
return slice
}
``````