Constraints

In the example on the previous page we tried to sum a series of 64-bit integers with the generic Sum[T int](...T) T function (Go playground):

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. // Sum returns the sum of the provided arguments.
  6. func Sum[T int](args ...T) T {
  7. var sum T
  8. for i := 0; i < len(args); i++ {
  9. sum += args[i]
  10. }
  11. return sum
  12. }
  13. func main() {
  14. fmt.Println(Sum([]int64{1, 2, 3}...))
  15. }

However, when trying to run this example the compiler balks with the following error:

  1. ./prog.go:17:17: int64 does not implement int

This occurs because while the function Sum[T int](...T) T is written generically, the type of T is constrained to be only an int. The function needs to be rewritten so it can accept an int or an int64:

  1. func Sum[T int | int64](args ...T) T {
  2. var sum T
  3. for i := 0; i < len(args); i++ {
  4. sum += args[i]
  5. }
  6. return sum
  7. }
  8. func main() {
  9. fmt.Println(Sum([]int{1, 2, 3}...))
  10. fmt.Println(Sum([]int64{1, 2, 3}...))
  11. }

Using the | operator, the constraints for T can be expressed such that T can be satisfied by an int or int64. Now when the example is run, it produces the expected output (Golang playground):

  1. 6
  2. 6

:sparkles: Wait a minute, isn’t the | operator in Go supposed to be the bitwise OR operation and || the conditional OR?

You are not wrong. However, it seems that the type parameter proposal elected to follow the lead of languages like PHP, Python, and TypeScript, which all use the | operator to express a union type. Indeed T | K is referred to as a union constraint in the type parameter proposal.


But what about wanting a Sum function that can handle any numeric value? While it is possible to just list them all in Sum[T int | int32 | int64 |..., this would result in difficult-to-read code. Luckily there is a better way…


Next: The any constraint