4

When I compile the following program

func myPrint(v ...interface{}) {
        fmt.Println("Hello", v...)
}
func main() {
    myPrint("new", "world")
}

I get a compilation error

too many arguments in call to fmt.Println

I thought v... is going to expand into 2nd, 3rd arguments and the fmt.Println would see three item variadic argument list. I thought it would be equivalent to

fmt.Println("Hello", "new", "world")

Why is it giving an error.

2 Answers 2

6

Try this. It prepends Hello to the variadic arguments, then prints them all at once with println.

package main

import "fmt"

func myPrint(v ...interface{}) {
    a := append([]interface{}{"Hello"}, v...)   // prepend "Hello" to variadics
    fmt.Println(a...)                           // println the whole lot
}
func main() {
    myPrint("new", "world")
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks that works. I also did the same to get by. But I would like to understand why the other doesn't work. Is this an expected behavior according to language specs or an anomaly of the compiler?
2

You're mis-using the variadic shorthand in your call to fmt.Println(). What you're actually sending is 2 arguments: a single string, then the slice of type interface{} expanded. The function call will not concatenate that into a single slice.

This design will compile and run with the results you're expecting:

func myPrint(v ...interface{}) {
    fmt.Print("Hello ")
    fmt.Println(v...)
}

func main() {
    myPrint("new", "world")
}

7 Comments

Thanks. So it seems a slice of n elements expanded is not same as passing n arguments. What is the definition of expansion. Is there any reason why the compiler has to treat an argument followed by an expansion not same as one single expanded list
No, because the list must be collapsed into a slice by the underlying system, which will either 1) collect a set of single arguments or 2) pass a slice. The easiest way to think about it is to match the ellipses if you're expanding a slice.
according to go specs, "We write ... after v in the nested call to Sprintln to tell the compiler to treat v as a list of arguments; otherwise it would just pass v as a single slice argument. " Based on the above statement the correct behavior should be treating it as a list of argument instead of a single slice argument. I even tried the following and it doesn't work ` func myPrint(v ...interface{}) { fmt.Println(interface{}("Hello"), v...) }`
The issue isn't the compiler, but the parser. The function call fmt.Println("Hello", v...) is parsed (using the comma) into two arguments, then sent to the compiler. Without this, there would need to be a complicated set of type-checking and validation to decide if the preceding argument belongs in the list or not. How would the compiler be able to match that call to the function if there was one function fmt.Println(s string, v...interface{}) and another fmt.Println(v...interface{})?
This is the same reason two expanded slices can't be used for the same variadic list, such as fmt.Println(v..., v...).
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.