Skip to content
Theory · Arrays and slices · lesson 8 of 10

Variadic functions

fmt.Println accepts one value, three or none, while a function with the parameters a, b int always requires exactly two arguments. A sum or a maximum is also handy to compute for any number of values.

A function that accepts any number of arguments of one type is called a variadic function. Its last parameter has three dots before the type:

GO
package main

import "fmt"

func sum(nums ...int) int {
    total := 0
    for _, v := range nums {
        total += v
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2, 3), sum(5), sum()) // 6 5 0
}

Picture a left-luggage office that gives your bags a row of lockers of the right length: two bags, two lockers; five bags, five. Go does the same. It puts the call's arguments into the lockers of a new row and hands the function a window onto that row, that is, a slice.

The parameter is a slice

Inside the function the parameter nums ...int is an ordinary []int slice. The call's arguments go into it in order, and len(nums) returns their count. Let's walk through the three calls in the example:

  1. sum(1, 2, 3): nums holds [1 2 3], the loop adds the three numbers, and the call returns 6.
  2. sum(5): nums has one element, and the call returns 5.
  3. sum(): the slice is empty, the range loop does not run at all, and total stays 0.

Everything that works with any slice works with nums: indexes, len, range and append. This is where the left-luggage analogy ends: in a call with no arguments no row is built at all, and nums is nil.

fmt.Println works the same way: its single parameter accepts any number of values of any type. That is why the call fmt.Println() with no arguments is allowed and prints an empty line.

Ordinary parameters before ...

Only the last parameter may have the three dots. Ordinary parameters may come before it: func scale(k int, nums ...int) takes the first number into k and the rest into nums. Let's look at two calls:

  • scale(10, 1, 2, 3) passes 10 into k and the slice [1 2 3] into nums;
  • scale() with no arguments does not compile: k is always required.

Two ways to call

A variadic function can be called in two ways.

Separate values

The call sum(1, 2, 3) lists the arguments separated by commas. Go builds a new row for them, and the function gets a window onto it. Each argument is copied into that row, so a write to nums does not change the caller's variables.

A ready slice with three dots

If the numbers are already in a slice, you pass them by putting three dots after the slice's name:

GO
s := []int{4, 5, 6}
fmt.Println(sum(s...)) // 15

The call sum(s...) adds 4, 5 and 6, and the program prints 15. s... does not copy the elements: Go builds no new row and hands the function your own window. The parameter nums gets the same slice s, so the write nums[0] = 0 inside the function would change s too.

Interviewers like to ask exactly this: does the caller see changes made through a variadic parameter? With s... it does.

This way has two rules:

  • the slice may be empty or nil: then nums is empty too, and sum returns 0;
  • separate values cannot be added to a slice with three dots: sum(1, s...) does not compile, while scale(10, s...) compiles, because 10 goes into k.

Common mistake

A slice is passed without the three dots:

GO
s := []int{4, 5, 6}
fmt.Println(sum(s)) // ✗
TEXT
cannot use s (variable of type []int) as int value in argument to sum

Every argument of the variadic function must have the type int, and s is a slice. The compiler does not spread a slice into elements on its own. Write sum(s...).

Key points

  • Inside the function, the parameter nums ...int is an ordinary []int slice; only the last parameter takes three dots.
  • A ready slice is passed as sum(s...), and the function gets the same slice, with no copy of the elements.
  • Without the three dots, a slice in place of separate int values does not compile.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way