Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot infer type in generic argument to function constructor

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?

like image 993
Woody1193 Avatar asked Sep 14 '25 09:09

Woody1193


1 Answers

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:

  • proposal: spec: a general approach to type inference, about changing the type inference algorithm to more easily allow future enhancements
  • inference based on assignment context, which depends on acceptance of the proposal above
like image 161
blackgreen Avatar answered Sep 17 '25 02:09

blackgreen