英文:
Go generics: infer type parameter for variadic function with zero arguments
问题
假设我有一个带有两个泛型参数的函数,其中一个是可变参数:
func Constructor[F any, Opt any](f F, opts ...Opt) {}
如果我传入一些选项,调用这个函数是可以的:
Constructor(func() *myService { return ... }, 1, 2, 3)
然而,如果没有传入任何Opt
,调用就会失败:
Constructor(func() *myService { return ... })
编译器会报错:
无法将'type func() *myService'(类型为func() *myService)用作类型(F, Opt)或F
我猜这是因为编译器在这种情况下无法确定Opt
的类型。
虽然这是有道理的,但仍然很烦人。编译器并不需要Opt
的类型,因为它是空的。
解决这个问题的一种方法是定义两个函数,Constructor
和ConstructorWithOpts
。不过,只有一个函数会更好。有什么想法吗?
英文:
Let's say I have a function with two generic parameters, one of them variadic:
func Constructor[F any, Opt any](f F, opts ...Opt) {}
Calling this function works fine if I pass in a few options:
Constructor(func() *myService { return ... }, 1, 2, 3)
However, calling it without any Opt
s fails:
Construtor(func() *myService { return ... })
The compiler complains:
> Cannot use 'func() *myService' (type func() *myService) as the type (F, Opt) or F
I assume that’s because the compiler can’t figure out the type of Opt
in this case.
While this makes sense, it’s annoying nevertheless. The compiler doesn't need the type of Opt
, since it's empty.
One way to work around this is to define two functions, Constructor
and ConstructorWithOpts
. It would be really nice to just have a single function though. Any ideas?
答案1
得分: 2
编译器不需要Opt的类型,因为它是空的。
即使调用者决定提供零个参数,函数体仍然可以使用可变参数的值进行操作。因此,编译器肯定需要Opt的类型。
此外,类型推断的规则是清楚的:
类型推断基于以下内容:
- 类型参数列表
- 已知的类型参数的替换映射M(如果有)
- (可能为空的)普通函数参数列表(仅适用于函数调用)
当某个类型参数的参数个数为零时,上述的第三个选项不适用。如果F和Opt完全不相关,那么第二个选项也不适用。
作为推论,考虑到如果调用者需要声明Constructor
函数类型的值,则必须提供所有的类型参数:
ctor := Constructor[func(), any] // 未调用!
ctor(f, 1, 2, 3)
最清晰的方法显然是让编译器推断F并明确指定Opts,尽管这需要反转函数声明中类型参数的顺序,以便客户端可以提供第一个参数并省略第二个参数。您可以定义一个any
类型以提高可读性:
func Constructor[Opt any, F any](f F, opts ...Opt) {}
type NoOpts any // 仅用于更好的可读性
func main() {
f := func() *myService { return ... }
// NoOpts实例化Opt;F由编译器推断
Constructor[NoOpts](f)
}
否则,只需使Constructor
不可变参数:
func Constructor[F any](f F) {
ConstructorWithOpts[F, any](f)
}
func ConstructorWithOpts[F any, Opt any](f F, opts ...Opt) {
// ...
}
英文:
> The compiler doesn't need the type of Opt, since it's empty.
Even if the caller decides to supply zero arguments, the body of the function can still operate with the value of the variadic parameter. So the compiler definitely needs the type of Opt
.
Furthermore the rules for type inference are clear:
> Type inference is based on
>
> - 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)
When you have zero arguments for a certain type parameter, the third option as of above isn't applicable. If F
and Opt
are completely unrelated, the second option is also not applicable.
As a corollary, consider that the caller must supply all the type args if they ever need to declare a value of your Constructor
function type:
ctor := Constructor[func(), any] // not called!
ctor(f, 1, 2, 3)
The cleanest is obviously to let the compiler infer F
and specify Opts
explicitly, although this requires inverting the order of the type parameters in the function declaration, so that clients can supply the first and omit the second. You may define an any
type to improve readability:
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)
}
Otherwise just make Constructor
non-variadic:
func Constructor[F any](f F) {
ConstructorWithOpts[F, any](f)
}
func ConstructorWithOpts[F any, Opt any](f F, opts ...Opt) {
// ...
}
答案2
得分: 1
你可以为可变参数提供一个空切片:
Constructor(func() *myService { return nil }, []int{}...)
你也可以传递nil
,但这样更冗长:
Constructor(func() *myService { return nil }, ([]int)(nil)...)
或者为类型参数提供值:
Constructor[func() *myService, any](func() *myService { return nil })
在Go Playground上尝试一下。
英文:
You may provide an empty slice for the variadic argument:
Constructor(func() *myService { return nil }, []int{}...)
You can also pass nil
, but it's more verbose:
Constructor(func() *myService { return nil }, ([]int)(nil)...)
Or provide values for the type parameters:
Constructor[func() *myService, any](func() *myService { return nil })
Try these on the Go Playground.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论