Go 1.18 and above
With the introduction of type parameters in Go 1.18, this is easier to accomplish.
You can define a function parametrized in T
and use an interface constraint to restrict T
to numeric types.
func add[T Number](a, b T) T {
return a + b
}
The constraint Number
can be defined using golang.org/x/exp/constraints
package (still experimental):
import "golang.org/x/exp/constraints"
type Number interface {
constraints.Integer | constraints.Float
}
Where:
Number
is the union of the type sets of constraints.Integer
and constraints.Float
constraints.Integer
is the set of all signed and unsigned integer types
contraints.Float
is the set of float types
This will allow you to call add
with any two arguments of numeric type. Then in the function body you will be able to use any operation that is supported by all types in the constraint. So in case of numbers, this includes also arithmetic operators. Then declaring similar functions is easy:
func multiply[T Number](a, b T) T {
return a * b
}
Keep in mind that the arguments must have the same type. Regardless of generics, you can't use different types; from the specs Operators:
[...] the operand types must be identical unless the operation involves shifts or untyped constants.
Therefore our generic add
and multiply
functions are defined with only one type parameter T
. This implies that you also can't call the add
function with untyped constants whose default types are incompatible:
add(2.5, 2) // won't compile
In this case the compiler will infer the type of T
from the first argument 2.5
, which defaults to float64
, and then won't be able to match the type of 2
, which defaults to int
.
Full program:
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
type Number interface {
constraints.Integer | constraints.Float
}
func main() {
a := 1
b := 2
fmt.Println(add(1, 2)) // 3
fmt.Println(add(a, b)) // 3
fmt.Println(add(1.5, 3.2)) // 4.7
// fmt.Println(add(2.5, 2)) // default type int of 2 does not match inferred type float64 for T
}
func add[T Number](a, b T) T {
return a + b
}
Playground: https://go.dev/play/p/rdqi3_-EdHp
Warning: since these functions handle also floats, keep in mind that floats can hold NaN
values and infinities.
About complex numbers
Go has complex64
and complex128
predeclared types. You can use them too in the Number
constraint:
import "golang.org/x/exp/constraints"
type Number interface {
constraints.Integer | constraints.Float | constraints.Complex
}
This doesn't restrict the capabilities of these generic functions: the arithmetic operators that are supported by integers and floats (only +
, -
, *
and /
) and all order operators are supported by complex types too. The remainder operator %
and bitwise operators are supported only by integers, and therefore by type parameters constrained to constraints.Integer
.