Variadic arguments are nothing new. They power all of our Printf statements. These are just syntactic sugar for arrays. For example, here is a variadic function that prints all of the strings passed to it:

So, if I call printStrings("one", "two", "ten") or printStrings("one", "twelve"), it will print everything out!

In Golang, interface{} is a valid type. Everything conforms to the empty interface, so it can represent any object. So, this is true:

And this is, too…

So, what happens if I attempt to wrap a variadic function with another variadic function?

If they are both of type interface{}, it works (with a caveat):

Output:

A direct call to a function with variadic arguments:
Data= [one two]
Direct Type= []interface {}

An indirect call to a function with variadic arguments:
Middleman Data= [one two]
Data= [[one two]]
Direct Type= []interface {}
Indirect Type= []interface {}


Specifically, the second call is nesting an array of interface{}s:

However, both show the type []interface{}, because a slice of []interface{} is still an interface{}. Weird, right?

In contrast, if I change it to specifically use strings, I get a nice error:

Specifically, a slice of strings is not the same as passing arguments to the variadic function:

prog.go:18: cannot use ok (type []string) as type string in argument to direct


Of course, I could just use slices. This is not very different, it is just not very appealing:

indirect([]string{"one", "two"})


However, this approach is much more extensible. I can wrap it as much as I want without added complexity.

## Questioning Design

However, this really made me question Go. It makes sense that the interface{} one works, but it can suppress important errors that may indicate a problem down the road. Even using reflection, I cannot tell the immediate difference between []interface{} and one nested within another. They are both of the interface{} type.

This has led me to be incredibly cautious when it comes to the interface{} type. This concept is far from strictly typed, and can introduce an array of problems into a program. It seems to be used for one thing: a compromise. It removes the need for generics, but, if it is used in excess, it undermines the beauty of Go’s type system.

What are your thoughts? Let me know.