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?
CodePudding user response:
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) {
// ...
}
CodePudding user response:
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.