假设我有一个函数,它有两个通用参数,其中一个是可变参数:
func Constructor[F any, Opt any](f F, opts ...Opt) {}
如果我传入几个选项,调用这个函数可以很好地工作:
Constructor(func() *myService { return ... }, 1, 2, 3)
但是,在没有任何Opt
s的情况下调用它失败:
Construtor(func() *myService { return ... })
编译器抱怨:
不能将'func((*myService'(type func(*myService((用作类型(F,Opt(或F
我认为这是因为编译器在这种情况下无法计算出Opt
的类型。
虽然这是有道理的,但也很烦人。编译器不需要Opt
的类型,因为它是空的。
解决此问题的一种方法是定义两个函数Constructor
和ConstructorWithOpts
。如果只有一个函数,那就太好了。有什么想法吗?
编译器不需要Opt类型,因为它是空的。
即使调用方决定提供零参数,函数体仍然可以使用变参数的值进行操作。所以编译器肯定需要Opt
的类型。
此外,类型推理的规则是明确的:
类型推断基于
- 类型参数列表
- 用已知的键入参数(如果有(
- 普通函数的列表(可能为空(自变量(仅在函数调用的情况下(
当某个类型参数的参数为零时,上述第三个选项不适用。如果F
和Opt
完全不相关,则第二个选项也不适用。
作为推论,如果调用方需要声明Constructor
函数类型的值,则考虑调用方必须提供所有类型参数:
ctor := Constructor[func(), any] // not called!
ctor(f, 1, 2, 3)
最干净的方法显然是让编译器推断F
并显式指定Opts
,尽管这需要反转函数声明中类型参数的顺序,这样客户端就可以提供第一个,而省略第二个。您可以定义一个any
类型来提高可读性:
func Constructor[Opt any, F any](f F, opts ...Opt) {}
type NoOpts any // just for better readability
func main() {
f := func() *myService { return ... }
// NoOpts instantiates Opt; F inferred
Constructor[NoOpts](f)
}
否则,只需使Constructor
不可变:
func Constructor[F any](f F) {
ConstructorWithOpts[F, any](f)
}
func ConstructorWithOpts[F any, Opt any](f F, opts ...Opt) {
// ...
}
您可以为可变参数提供一个空切片:
Constructor(func() *myService { return nil }, []int{}...)
您也可以通过nil
,但它更详细:
Constructor(func() *myService { return nil }, ([]int)(nil)...)
或者提供类型参数的值:
Constructor[func() *myService, any](func() *myService { return nil })
在围棋场上试试这些。