How ‘…’ saved my data(type) when somebody tried to modify it?

Manivannan Selvaraj
3 min readAug 3, 2019

I started programming in Golang early this year and I’m loving it. It has been a couple of months and I’m still learning something new everyday :)

Yesterday I had a use case where there could be any number of any type of arguments and my function had to do something based on the the type of the argument. I found variadic function with interfaces was the perfect fit for the use case.

I wrote the code which looked like below.

package mainimport (
"fmt"
)
func main() {
myVar := "mani is here"
testVarArgs(myVar) // Call the function directly
somebody(myVar) // Call it through somebody()
}func testVarArgs(args ...interface{}) {
for _, arg := range args {
switch object := arg.(type) {
case string:
fmt.Println("Type is string")
default:
fmt.Printf("Unsupported type: %T\n", object)
}
}
}
func somebody(args ...interface{}) {
testVarArgs(args)
}

Output

Type is string
Unsupported type: []interface {}

Wait! Did somebody() change the type of my argument ?

I found that the data main() passed to the function somebody() isn’t the same type when it reached testVarArgs().

Is Go crazy or I am missing something 🤔? Go cannot be crazy, so definitely I am missing something.

What did testVarArgs() receive ?

When main() called testVarArgs() with ‘myVar’ (which was a string), what really happened was that the compiler created a slice of string with ‘myVar’ as the only element (since ‘myVar’ was a string) and sent it to testVarArgs(). What reached testVarArgs() should have looked like below.

[]string{"mani is here"}

While going through the loop and switch case, the type would be identified as ‘string’ which is what we expected. Cool!

Alright! Now what did somebody() do when it called testVarArgs ?

As discussed previously, somebody() should have received

[]string{“mani is here”}

But when somebody() called testVarArgs(), it would have sent the argument like below.

[]interface{[]string{"mani is here"}}

What ?? Why ?? 🤔

That is how Go compiler passes data to a variadic function. It creates a slice of the data type and adds the data to it before passing it to the functions. Like in main() it created a slice of string when it saw the data type string.

Now that the data is already a string slice in somebody(), compiler has created a slice of interface and passed it to testVarArgs().

How can I preserve the type when it is passed through multiple functions ?

The answer is …

The answer is …

The answer is …

Yeah, just add ellipsis to the variable where it is received and passed to another variadic function with interface. That’ll send the type as it is and won’t create a new slice.

package mainimport (
"fmt"
)
func main() {
myVar := "mani is here"
testVarArgs(myVar)
whatsWrong(myVar)
gotcha(myVar)
}func testVarArgs(args ...interface{}) {
for _, arg := range args {
switch object := arg.(type) {
case string:
fmt.Println("Type is string")
default:
fmt.Printf("Unsupported type: %T\n", object)
}
}
}
func whatsWrong(args ...interface{}) {
testVarArgs(args)
}
func gotcha(args ...interface{}) {
testVarArgs(args...)
}

Output

Type is string
Unsupported type: []interface {}
Type is string

That’s how ‘’ saved my type when somebody tried to modify it 🙂 !

--

--