Go

How does type inference work? When does it fail, and how do you order type parameters to make partial instantiation pleasant?

Question 119HardGo 1.22 to 1.25

Go infers type arguments in two ways:

  • Function argument inference unifies parameter types with argument types.
  • Constraint type inference derives a parameter from another parameter's constraint. For example, it gets E from S ~[]E.

Inference never uses the result type or the assignment target. The exception, since Go 1.21, is assigning a generic function to a variable or parameter of explicit function type.

func Zero[T any]() T { var z T; return z }
// v := Zero()           // error: cannot infer T
v := Zero[int]()

// Put non-inferable params FIRST so callers can partially instantiate
func Convert[To, From ~int | ~float64](f From) To { return To(f) }
x := Convert[float64](3) // To=float64 explicit, From=int inferred

// Go 1.21: mixed untyped constants choose the "larger" kind
fmt.Println(max2(1, 2.5)) // T inferred as float64 -> 2.5
func max2[T cmp.Ordered](a, b T) T { if a > b { return a }; return b }

// Go 1.21: generic func passed to generic func
xs := []int{3, 1, 2}
slices.SortFunc(xs, cmp.Compare) // cmp.Compare[int] inferred

Gotchas:

  • With untyped nil nothing can be inferred.
  • Inference does not look through interfaces. If a method-only constraint is the only clue, you must pass type arguments explicitly.
  • Before 1.21, max2(1, 2.5) failed to compile.

More on Generics

All 36 Generics questions