I have some generic code in Go wherein there is a "master" type that has a generic argument and a number of "slave" types that should share the same generic argument. The code looks similar to this:
type Doer[T any] interface {
ModifyA(*A[T])
}
type B[T any] struct {
}
func NewB[T any]() *B[T] {
return new(B[T])
}
func (b *B[T]) ModifyA(a *A[T]) {
// Do a thing
}
type A[T any] struct{}
func NewA[T any]() A[T] {
return A[T]{}
}
func (a *A[T]) Run(doers ...Doer[T]) {
for _, doer := range doers {
doer.ModifyA(a)
}
}
func main() {
a := new(A[int])
a.Run(NewB()) // error here
}
Basically, the user is supposed to define T
on A
and then the T
on B
should be the same. This sort of code would work in other languages that support generics, but in Go I get a cannot infer T
compilation error at the commented line (see Go playground code, here). To my mind, the type parameter on a
is set to int
so the type parameter on B
should also be set to int
. I could call NewB[int]()
instead, but this seems excessively verbose to me. Why is this happening?
This is a variant of "why can't the compiler infer type parameters based on how return types are used?". Answer: because this isn't how type inference works, as of Go 1.20.
Type inference works with:
- a type parameter list
- a substitution map M initialized with the known type arguments, if any
- a (possibly empty) list of ordinary function arguments (in case of a function call only)
If you examine these rules one by one:
Does NewB()
have a type parameter list? No. You are calling it without specifying type arguments.
Are there other known type arguments to infer other type parameters with? No. You didn't supply any type arguments at all. Note that this case applies to function calls where you supply a partial number of type arguments, for example:
func foo[T any, U *T]() {}
In the above you could supply only T
, e.g. float64
, the compiler would construct a substitution map with T -> float64
and then infer that U -> *float64
Finally, is there a list of ordinary function arguments? No. NewB
is nullary.
That's all. The compiler does not infer type parameters based on how the function return type is used.
Relevant proposals that, at the time of writing, are under discussion are:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With