ZetCode

Go ellipses

last modified October 30, 2026

In this article we show how to use the ellipses operator in Go. Three dots (...) appear in variadic functions, in composite literals and in the package patterns of the go tool.

All examples were compiled and run with Go 1.27.0.

Variadic parameters

A variadic function accepts any number of trailing arguments. The parameter is declared with ... before its type, and inside the function it is an ordinary slice.

main.go
package main

import "fmt"

func Sum(nums ...int) int {

    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {

    fmt.Println(Sum())
    fmt.Println(Sum(1))
    fmt.Println(Sum(1, 2, 3))
}

The function can be called with no arguments at all, in which case the slice is nil and the loop does not run. With one argument it behaves like a function of one parameter.

$ go run main.go
0
1
6

Passing a slice

A slice can be passed to a variadic parameter by appending ... to the argument. Without the dots the slice is passed as a single value, which does not match the parameter type.

main.go
package main

import "fmt"

func Sum(nums ...int) int {

    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {

    vals := []int{4, 5, 6}
    fmt.Println(Sum(vals))
}

The compiler sees one argument of type []int where the function expects int values. Writing Sum(vals...) expands the slice into three arguments, so the program compiles and prints 15.

$ go build
./main.go:17:21: cannot use vals (variable of type []int) as int value in argument to Sum

Mixing fixed and variadic parameters

Only the last parameter of a function can be variadic. A function can combine fixed parameters with a variadic tail, which is useful for separators, prefixes and options.

main.go
package main

import "fmt"

func Join(sep string, parts ...string) string {

    out := ""
    for i, p := range parts {
        if i > 0 {
            out += sep
        }
        out += p
    }
    return out
}

func main() {

    fmt.Println(Join("-", "a", "b", "c"))
    fmt.Println("[" + Join(", ") + "]")
    fmt.Println(Join(", ", []string{"x", "y"}...))
}

The separator is a fixed argument and the strings are variadic. The second call has no variadic arguments, so the result is an empty string between the brackets. The third call expands a slice.

$ go run main.go
a-b-c
[]
x, y

append and fmt are variadic

Several functions of the standard library are variadic. append takes one or more values, or another slice followed by ..., and fmt.Println accepts any number of values of any type.

main.go
package main

import "fmt"

func main() {

    nums := []int{1, 2, 3}

    nums = append(nums, 4, 5)
    fmt.Println(nums)

    nums = append(nums, []int{6, 7}...)
    fmt.Println(nums)

    fmt.Println("values:", nums, "count:", len(nums))
}

The first append adds two values, the second adds all elements of another slice. Printing several values in one call works because Println is variadic as well.

$ go run main.go
[1 2 3 4 5]
[1 2 3 4 5 6 7]
values: [1 2 3 4 5 6 7] count: 7

Inferring array length

In an array composite literal, [...] replaces the length and lets the compiler count the elements. The result is still a fixed-size array, not a slice.

main.go
package main

import "fmt"

func main() {

    a := [...]int{3, 1, 4, 1, 5}
    fmt.Println(len(a), a)

    b := [...]string{2: "two", 0: "zero"}
    fmt.Println(len(b), b)

    c := [...]int{}
    fmt.Println(len(c), c)
}

The first array has five elements and inferring gives it a length of five. With indexed elements the length is one more than the highest index, so the second array has three elements and an empty string in the middle. An empty literal produces an array of length zero.

$ go run main.go
5 [3 1 4 1 5]
3 [zero  two]
0 []

Variadic function types

The three dots are part of the function type. A variable of type func(...int) int can hold any function with that signature.

main.go
package main

import "fmt"

func Sum(nums ...int) int {

    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {

    var f func(...int) int = Sum

    fmt.Println(f(7, 8))
    fmt.Printf("%T\n", f)
}

The variable is assigned the Sum function and called like any other function. The type printed by %T includes the dots.

$ go run main.go
15
func(...int) int

Wildcards in go commands

The go tool uses ... as a wildcard for package patterns. ./... means the current module and all its subpackages, while a bare ... matches packages in the standard library and in the module cache.

The pattern is handled by the go tool, not by the Go language, so it can be used with build, vet, test and the other package commands. Quoting is unnecessary because the shell passes the dots through unchanged.

$ go list ./...
com.zetcode/ellipses
$ go test ./...
?   	com.zetcode/ellipses	[no test files]
$ go list ... | head -3
archive/tar
archive/zip
bufio

Source

The Go Programming Language Specification - function types
Go command - package patterns

In this article we have covered the ellipses operator in Go. We used it for variadic parameters and arguments, for mixing fixed and variadic parameters, for appending slices, for inferring array lengths, in function types and as a package wildcard in the go tool.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Go tutorials.