Variadic functions in Go

Introduction for aspiring Gophers

Function is variadic if and only if the last parameter is of type …T (three dots before T are not there by accident):

package main
import "fmt"
func sum(numbers ...float64) (res float64) {
for _, number := range numbers {
res += number
}
return
}
func main() {
fmt.Println(sum(1.1, 2.2, 3.3))
}

It allows to pass arbitrary (variable) number of arguments. They’re available inside function through elements of a slice (numbers parameter in code above).

Only the last parameter can be preceded with … (three dots) denoting variadic function.

argument vs. parameter

Most of the time these terms can be used interchangeably but arguments usually refer to actual values in function / method call and parameters are specified in function declaration:

package main
import "fmt"
func sum(a, b float64) (res float64) {
return a + b
}
func main() {
fmt.Println(sum(1.1, 1.2))
}
  • a and b are parameters
  • res is a named result parameter
  • 1.1 and 1.2 are arguments

… becomes a slice

The actual type of …T inside the function is []T:

package main
import "fmt"
func f(names ...string) {
fmt.Printf("value: %#v\n", names)
fmt.Printf("length: %d\n", len(names))
fmt.Printf("capacity: %d\n", cap(names))
fmt.Printf("type: %T\n", names)
}
func main() {
f("one", "two", "three")
}

The compiled code produces:

> go install github.com/mlowicki/lab && ./bin/lab
value: []string{"one", "two", "three"}
type: []string
length: 3
capacity: 3

Type identity

Type of variadic function isn’t equal to the one which takes slice as a last parameter:

f := func(...int) {}
f = func([]int) {}

It’s detected while building:

src/github.com/mlowicki/lab/lab.go:17: cannot use func literal (type func([]int)) as type func(...int) in assignment

Let’s put dots on the other side…

Next snippet cannot be compiled successfully:

package main
import "fmt"
func f(numbers ...int) {
fmt.Println(numbers)
}
func main() {
numbers := []int{1, 2, 3}
f(numbers)
}
> go install github.com/mlowicki/lab && ./bin/lab
# github.com/mlowicki/lab
src/github.com/mlowicki/lab/lab.go:11: cannot use numbers (type []int) as type int in argument to f

It’s because a single argument needs to have a int type so slice of integers is obviously not allowed. There is a mechanism directly in the language to make it working:

package main
import "fmt"
func f(numbers ...int) {
fmt.Println(numbers)
}
func main() {
numbers := []int{1, 2, 3}
f(numbers...)
}

The change which is easily to overlook are three dots (…) after the argument in call to function f. It passes numbers as a list of arguments. This way it’s possible to call variadic function passing slice of elements.