无法推断函数构造函数中的泛型参数的类型。

huangapple go评论93阅读模式
英文:

Cannot infer type in generic argument to function constructor

问题

我在Go中有一些通用代码,其中有一个"master"类型,它有一个通用参数,还有一些"slave"类型,这些类型应该共享相同的通用参数。代码看起来类似于这样:

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
}

基本上,用户应该在A上定义T,然后B上的T应该是相同的。这种代码在其他支持泛型的语言中可以工作,但在Go中,我在注释行处得到一个"cannot infer T"的编译错误(请参见Go Playground代码,这里)。在我看来,a上的类型参数被设置为int,所以B上的类型参数也应该被设置为int。我可以调用NewB[int]()来代替,但我觉得这样太啰嗦了。为什么会发生这种情况?

英文:

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?

答案1

得分: 2

这是一个变种的问题:“为什么编译器不能根据返回类型的使用来推断类型参数?” 答案是:因为这不是类型推断的工作方式,截至 Go 1.20。

类型推断 的工作方式如下:

  • 类型参数列表
  • 已知类型参数的替换映射 M(如果有的话)
  • (可能为空的)普通函数参数列表(仅适用于函数调用)

逐条检查这些规则:

  • NewB() 有类型参数列表吗?没有。你在调用它时没有指定类型参数。

  • 是否有其他已知的类型参数可以推断其他类型参数?没有。你根本没有提供任何类型参数。请注意,这种情况适用于你提供部分类型参数的函数调用,例如:

      func foo[T any, U *T]() {}
    

    在上面的例子中,你可以只提供 T,例如 float64,编译器会构建一个替换映射 T -> float64,然后推断出 U -> *float64

  • 最后,是否有普通函数参数列表?没有。NewB 是无参数的函数。

就是这样。编译器不会根据函数返回类型的使用来推断类型参数。

目前正在讨论的相关提案有:

英文:

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:

huangapple
  • 本文由 发表于 2023年4月10日 11:16:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/75974073.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定