Passing arguments to ... parameters

December 26, 2023

If you’ve been a reader for a while, you may recall back in April when I first talked about variadic functions. Now we’re ready to dig in a bit further to the details of how these mysterious creatures work.

First we’ll see how they work “from the inside”. That is, from the perspective of someone writing a variadic function.

Passing arguments to `...` parameters

If `f` is variadic with a final parameter `p` of type .`...T`, then within `f` the type of `p` is equivalent to type `[]T`. If `f` is invoked with no actual arguments for `p`, the value passed to `p` is `nil`. Otherwise, the value passed is a new slice of type `[]T` with a new underlying array whose successive elements are the actual arguments, which all must be assignable to `T`. The length and capacity of the slice is therefore the number of arguments bound to p and may differ for each call site.

Given the function and calls

``````func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
``````

within `Greeting`, who will have the value `nil` in the first call, and `[]string{"Joe", "Anna", "Eileen"}` in the second.

So in other words, from the perspective of the function author, these can be considered functionally equivalent, with identical implementations:

``````func Greeting(prefix string, who ...string) {
fmt.Printf("%s %s\n", prefix, strings.Join(who, ", "))
}

func Greeting(prefix string, who []string) {
fmt.Printf("%s %s\n", prefix, strings.Join(who, ", "))
}
``````

Whether `who` is a variadic argument, or an explicit `[]string`, the type of the argument is the same.

The only difference between these two is that the explicit `[]string` type may receive a non-nil, but empty value, whereas the variadic version cannot—it will always receive `nil`, or a slice with at least one value.

``````func Greeting(prefix string, who ...string) {
if who == nil {
/* ... what to do when we're greeting nobody? ... */
} else if len(who) == 0 {
/* ... Cannot happen for a variadic input ... */
} else {
fmt.Printf("%s %s\n", prefix, strings.Join(who, ", "))
}
}
``````

Quotes from The Go Programming Language Specification Version of August 2, 2023