我在 Go 中有一些通用代码,其中有一个具有通用参数的“主”类型和许多应共享相同通用参数的“从”类型。代码看起来类似于:
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
}
基本上,用户应该在
T
上定义A
然后在T
上定义B
应该是相同的。这种代码可以在其他支持泛型的语言中运行,但在 Go 中,我在注释行中得到一个cannot infer T
编译错误(参见 Go playground 代码,here)。在我看来,a
上的类型参数设置为 int
,因此 B
上的类型参数也应设置为 int
。我可以改为调用NewB[int]()
,但这对我来说似乎过于冗长。为什么会这样?
这是“为什么编译器不能根据返回类型的使用方式推断类型参数?”的变体。答案:因为这不是类型推断的工作方式,从 Go 1.20 开始。
类型推断适用于:
- 类型参数列表
- 用已知类型参数初始化的替换映射 M,如果有的话
- 一个(可能为空)普通函数参数列表(仅在函数调用的情况下)
NewB()
有类型参数列表吗?不,你在调用它时没有指定类型参数。
是否有其他已知类型参数可以用来推断其他类型参数?不,您根本没有提供任何类型参数。请注意,这种情况适用于您提供部分类型参数的函数调用,例如:
func foo[T any, U *T]() {}
在上面你可以只提供
T
,例如float64
,编译器将用 T -> float64
构造一个替换映射,然后推断出 U ->
*float64`
最后,有普通函数参数列表吗?不。
NewB
无效。
就是这样。编译器不会根据函数返回类型的使用方式推断类型参数。