I am curious about unpacking a slice of slices and sending them as arguments to a variadic function.
Let's say we have a function with variadic parameters:
func unpack(args ...interface{})
If we wan't to pass in a slice of interfaces it works, it doesn't matter if we unpack it or not:
slice := []interface{}{1,2,3}
unpack(slice) // works
unpack(slice...) // works
It gets tricky if we have a slice of slices. Here the compiler doesn't let us pass in an unpacked version:
sliceOfSlices := [][]interface{}{
[]interface{}{1,2},
[]interface{}{101,102},
}
unpack(sliceOfSlices) // works
unpack(sliceOfSlices...) // compiler error
The error says:
cannot use sliceOfSlices (type [][]interface {}) as type []interface {} in argument to unpack
I don't know why this happens, as we can clearly pass an []interface{} type into the function. How can I call the method unpack with the unpacked content of sliceOfSlices as arguments?
Playground example: https://play.golang.org/p/O3AYba8h4i
This is covered in Spec: Passing arguments to ... parameters:
If
fis variadic with a final parameterpof type...T, then withinfthe type ofpis equivalent to type[]T....
If the final argument is assignable to a slice type
[]T, it may be passed unchanged as the value for a...Tparameter if the argument is followed by.... In this case no new slice is created.
So in short: it is a compile-time error because sliceOfSlices (which is of type [][]interface{}) cannot be assigned to args (which is of type []interface{}) (proof on Playground).
In long:
In your first example when you do unpack(slice), since unpack() expects values of interface{}, therefore slice (which is of type []interface{}) will be wrapped in a new interface{} value, and it will be passed as a single argument.
When you do unpack(slice...), this will pass all the values of slice as separate values to unpack(); this is possible because type of slice is []interface{}, it matches the type of the variadic parameter (args ...interface{}).
In your second example when you do unpack(sliceOfSlices), again, sliceOfSlices will be wrapped in a new interface{} value and passed as a single argument.
But when you try unpack(sliceOfSlices...), that would want to pass each element of sliceOfSlices to unpack(), but type of sliceOfSlices (which is [][]interface{}) does not match the type of the variadic parameter, hence the compile-time error.
The only way to pass sliceOfSlices to unpack() "exploded" is to create a new slice whose type must be []interface{}, copy the elements, then you can pass it using ....
Example:
var sliceOfSlices2 []interface{}
for _, v := range sliceOfSlices {
sliceOfSlices2 = append(sliceOfSlices2, v)
}
unpack(sliceOfSlices2...)
Try it on the Go Playground.
Let's use the following unpack() function to verify the number of arguments:
func unpack(args ...interface{}) {
fmt.Println(len(args))
}
Running your example (and with my new slice creation), output is:
1
3
1
2
Which proves without ... only a single argument is passed (wrapped in interface{}), and using ... all elements will be passed separately.
Try this test on the Go Playground.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With