Welcome to part 12 of our Golang tutorial series.

### What is a variadic function?

Functions in general accept only a fixed number of arguments. A variadic function is a function that accepts a variable number of arguments. If the last parameter of a function definition is prefixed by ellipsis ..., then the function can accept any number of arguments for that parameter.

Only the last parameter of a function can be variadic. We will learn why this is the case in the next section of this tutorial.

### Syntax

``````func hello(a int, b ...int) {
}
``````

In the above function, the parameter `b` is variadic since it's prefixed by ellipsis and it can accept any number of arguments. This function can be called by using the syntax.

``````hello(1, 2) //passing one argument "2" to b
hello(5, 6, 7, 8, 9) //passing arguments "6, 7, 8 and 9" to b
``````

In the above piece of code, in line no. 1 we call `hello` with one argument `2` for the parameter `b` and we pass four arguments `6, 7, 8, 9` to `b` in the next line.

It is also possible to pass zero arguments to a variadic function.

``````hello(1)
``````

In the above code, we call `hello` with zero arguments for `b`. This is perfectly fine.

By now I guess you would have understood why the variadic parameter should only be at the last.

Let's try to make the first parameter of the `hello` function variadic.

The syntax will look like

``````func hello(b ...int, a int) {
}
``````

In the above function, it is not possible to pass arguments to the parameter `a` because whatever argument we pass will be assigned to the first parameter `b` since it's variadic. Hence variadic parameters can only be present at the last in the function definition. The above function will fail to compile with error `syntax error: cannot use ... with non-final parameter b`

### Examples and understanding how variadic functions work

Let's create our own variadic function. We will write a simple program to find whether an integer exists in an input list of integers.

``````package main

import (
"fmt"
)

func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)
find(45, 56, 67, 45, 90, 109)
find(78, 38, 56, 98)
find(87)
}
``````

Run in playground

In the above program, `func find(num int, nums ...int)` in line no.7, accepts variable number of arguments for the `nums` parameter. Inside the function find, the type of `nums` is `[]int` i.e, an integer slice.

The way variadic functions work is by converting the variable number of arguments to a slice of the type of the variadic parameter. For instance, in line no. 22 of the program above, the variable number of arguments to the `find` function are 89, 90, 95. The find function expects a variadic `int` argument. Hence these three arguments will be converted by the compiler to a slice of type int `[]int{89, 90, 95}` and then it will be passed to the `find` function.

In line no. 10, the `for` loop ranges over the `nums` slice and prints the position of `num` if it is present in the slice. If not, it prints that the number is not found.

The above program outputs,

``````type of nums is []int
89 found at index 0 in [89 90 95]

type of nums is []int
45 found at index 2 in [56 67 45 90 109]

type of nums is []int
78 not found in  [38 56 98]

type of nums is []int
87 not found in  []
``````

In line no. 25 of the above program, the find function call has only one argument. We have not passed any argument to the variadic `nums ...int` parameter. As discussed earlier, this is perfectly legal and in this case, `nums` will be a `nil` slice with length and capacity 0.

### Slice arguments vs Variadic arguments

We should definitely have a question lingering in your mind now. In the above section, we learned that the variadic arguments to a function are in fact converted a slice. Then why do we even need variadic functions when we can achieve the same functionality using slices? I have rewritten the program above using slices below.

``````package main

import (
"fmt"
)

func find(num int, nums []int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, []int{89, 90, 95})
find(45, []int{56, 67, 45, 90, 109})
find(78, []int{38, 56, 98})
find(87, []int{})
}
``````

Run in playground

The following of the advantages of using variadic arguments instead of slices.

1. There is no need to create a slice during each function call. If you look at the program above, we have created new slices during each function call in line nos. 22, 23, 24 and 25. This additional slice creation can be avoided when using variadic functions
2. In line no.25 of the program above, we are creating an empty slice just to satisfy the signature of the `find` function. This is totally not needed in the case of variadic functions. This line can just be `find(87)` when variadic function is used.
3. I personally feel that the program with variadic functions is more readable than the once with slices :)

### Append is a variadic function

Have you ever wondered how the append function in the standard library used to append values to a slice accepts any number of arguments. It's because it's a variadic function.

``````func append(slice []Type, elems ...Type) []Type
``````

The above is the definition of `append` function. In this definition `elems` is a variadic parameter. Hence append can accept a variable number of arguments.

### Passing a slice to a variadic function

Let's pass a slice to a variadic function and find out what happens from the below example.

``````package main

import (
"fmt"
)

func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums)
}
``````

Run in playground

In line no. 23, we are passing a slice to a function that expects a variable number of arguments.

This will not work. The above program will fail with compilation error `./prog.go:23:10: cannot use nums (type []int) as type int in argument to find`

Why does this don't work? Well, it's pretty straight forward. The signature of the `find` function is provided below,

``````func find(num int, nums ...int)
``````

According to the definition of a variadic function, `nums ...int` means that it will accept a variable number of arguments of type `int`.

In line no. 23 of the program above, `nums` which is []int slice is passed to the `find` function which is expecting a variadic `int` argument. As we already discussed, these variadic arguments will be converted to a slice of type `int` since `find` expects variadic int arguments. In this case, `nums` is already a `[]int` slice and the compiler tries to create a new `[]int` i.e the compiler tries to do

``````find(89, []int{nums})
``````

which will fail since `nums` is a `[]int` and not a `int`.

So is there a way to pass a slice to a variadic function? The answer is yes.

There is a syntactic sugar which can be used to pass a slice to a variadic function. You have to suffix the slice with ellipsis `...` If that is done, the slice is directly passed to the function without a new slice being created.

In the above program if you replace `find(89, nums)` in line no. 23 with `find(89, nums...)`, the program will compile and print the following output.

``````type of nums is []int
89 found at index 0 in [89 90 95]
``````

Here is the full program for your reference.

``````package main

import (
"fmt"
)

func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
nums := []int{89, 90, 95}
find(89, nums...)
}
``````

Run in playground

### Gotcha

Just be sure you know what you are doing when you are modifying a slice inside a variadic function.

Let's look at a simple example.

``````package main

import (
"fmt"
)

func change(s ...string) {
s = "Go"
}

func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
``````

Run in playground

What do you think will be the output of the above program? If you think it will be `[Go world]` Congrats! you have understood variadic functions and slices. If you got it wrong, no big deal, let me explain how we get this output.

In line no. 13 of the program above, we are using the syntactic sugar `...` and passing the slice as a variadic argument to the `change` function.

As we have already discussed, if `...` is used, the `welcome` slice itself will be passed as an argument without a new slice being created. Hence `welcome` will be passed to the `change` function as argument.

Inside the change function, the first element of the slice is changed to `Go`. Hence this program outputs

``````[Go world]
``````

Here is one more program to understand variadic functions.

``````package main

import (
"fmt"
)

func change(s ...string) {
s = "Go"
s = append(s, "playground")
fmt.Println(s)
}

func main() {
welcome := []string{"hello", "world"}
change(welcome...)
fmt.Println(welcome)
}
``````

Run in playground

I would leave it as an exercise for you to figure out how the above program works :).

That's it for variadic function. Thanks for reading. Please leave your valuable feedback and comments. Have a good day.

Next tutorial - Maps