Skip to main content
Variadic functions can be called with any number of trailing arguments. This powerful feature is used throughout the Go standard library, with fmt.Println being one of the most common examples.

Defining Variadic Functions

Use the ... syntax before the parameter type to accept a variable number of arguments:
func sum(nums ...int) {
    fmt.Print(nums, " ")
    total := 0
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}
Within the function, the type of nums is equivalent to []int (a slice). You can call len(nums), iterate over it with range, and use any other slice operations.

Calling Variadic Functions

Variadic functions can be called with any number of arguments:
sum(1, 2)
// Output: [1 2] 3

sum(1, 2, 3)
// Output: [1 2 3] 6

Slice Expansion

If you already have arguments in a slice, you can expand them into a variadic function using ...:
nums := []int{1, 2, 3, 4}
sum(nums...)
// Output: [1 2 3 4] 10
The nums... syntax unpacks the slice so its elements are passed as individual arguments.

Complete Example

package main

import "fmt"

// Variadic function accepting any number of ints
func sum(nums ...int) {
    fmt.Print(nums, " ")
    total := 0
    // nums is treated as a slice []int
    for _, num := range nums {
        total += num
    }
    fmt.Println(total)
}

func main() {
    // Call with individual arguments
    sum(1, 2)
    sum(1, 2, 3)

    // Expand a slice into arguments
    nums := []int{1, 2, 3, 4}
    sum(nums...)
}

Practical Examples

String Formatting

func format(template string, args ...interface{}) string {
    return fmt.Sprintf(template, args...)
}

result := format("Hello %s, you are %d years old", "Alice", 30)

Logging

func log(level string, messages ...string) {
    fmt.Printf("[%s] ", level)
    for _, msg := range messages {
        fmt.Print(msg, " ")
    }
    fmt.Println()
}

log("INFO", "Server", "started", "successfully")

Variadic Parameters with Other Parameters

Variadic parameters must be the last parameter in the function signature:
// Correct: variadic parameter is last
func greet(greeting string, names ...string) {
    for _, name := range names {
        fmt.Println(greeting, name)
    }
}

greet("Hello", "Alice", "Bob", "Charlie")
You can only have one variadic parameter per function, and it must be the last parameter. This won’t compile:
func invalid(nums ...int, str string) // Error!

Working with Empty Arguments

Variadic functions can be called with zero arguments:
func printAll(values ...string) {
    if len(values) == 0 {
        fmt.Println("No values provided")
        return
    }
    for _, v := range values {
        fmt.Println(v)
    }
}

printAll()  // Valid: prints "No values provided"

Best Practices

Variadic functions are ideal when you want to provide a convenient API that accepts varying numbers of arguments:
func max(nums ...int) int {
    if len(nums) == 0 {
        return 0
    }
    maxNum := nums[0]
    for _, num := range nums[1:] {
        if num > maxNum {
            maxNum = num
        }
    }
    return maxNum
}
Each call to a variadic function creates a new slice. For performance-critical code, consider accepting a slice directly instead.
Check the length of variadic parameters if your function requires a minimum number of arguments:
func average(nums ...float64) (float64, error) {
    if len(nums) == 0 {
        return 0, errors.New("need at least one number")
    }
    // Calculate average...
}

Key Takeaways

  • Variadic functions accept any number of arguments using ...Type syntax
  • Inside the function, variadic parameters behave like slices
  • Use slice... to expand a slice into individual arguments
  • Variadic parameters must be the last parameter in the signature
  • You can only have one variadic parameter per function

Build docs developers (and LLMs) love