英文:
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
是无参数的函数。
就是这样。编译器不会根据函数返回类型的使用来推断类型参数。
目前正在讨论的相关提案有:
- proposal: spec: a general approach to type inference,关于改变类型推断算法以更容易允许未来增强的提案
- inference based on assignment context,这取决于上述提案的接受情况
英文:
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 withT -> float64
and then infer thatU -> *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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论