>_
GolangStepByStep
Intern

Variadic Functions

Accept an arbitrary number of arguments with the ... syntax

# Why Variadic Functions Matter

Variadic functions let you accept zero or morearguments of the same type. They make call sites cleaner for APIs like logging, formatting, aggregation, and helper utilities.

In Go, the variadic parameter is still just a slice inside the function body. This means all slice rules still apply: length checks, range loops, and potential mutation of shared backing arrays.

Real-World Analogyclick to expand

Think of a variadic function like a shopping basket checkout: you can bring 0 items, 3 items, or 100 items, but the cashier processes them using one collection internally.

The call site is flexible, but inside the function it's always a slice. That mental model avoids confusion.

# Basic Variadic Syntax

Use ...T on the last parameter to accept any number of values of type T.

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

fmt.Println(sum(1, 2, 3))
fmt.Println(sum(1, 2, 3, 4, 5))
fmt.Println(sum())

# Step-by-Step Semantics

Variadic must be last parameter

func f(prefix string, vals ...int) is valid. Any parameter after ...int is invalid.

Inside function it is []T

You can use len(vals), for range, indexing, and all normal slice operations.

Passing a slice requires ...

Use f(slice...) to spread. Without ..., types won't match.

# Spreading a Slice and append

Spreading is the bridge between existing slices and variadic APIs:

nums := []int{1, 2, 3, 4, 5}
fmt.Println(sum(nums...))

a := []int{1, 2}
b := []int{3, 4}
a = append(a, b...) // slice concat

`append` is a built-in variadic function; always capture its returned slice because capacity growth can reallocate backing arrays.

# Mixed Parameters and Minimum-1 Pattern

You can combine fixed params with variadic params, and require at least one value by splitting first from rest.

func greet(prefix string, names ...string) {
    for _, name := range names {
        fmt.Printf("%s, %s!
", prefix, name)
    }
}

func Max(first int, rest ...int) int {
    max := first
    for _, v := range rest {
        if v > max {
            max = v
        }
    }
    return max
}

# Real-World Example: Variadic Join

Implement a variadic wrapper similar to strings.Join.

package main

import (
    "fmt"
    "strings"
)

func Join(sep string, parts ...string) string {
    if len(parts) == 0 {
        return ""
    }
    return strings.Join(parts, sep)
}

func main() {
    fmt.Println(Join(", ", "a", "b", "c"))
    fmt.Println(Join("-", "2024", "01", "15"))
    fmt.Println(Join(", "))
}

⚡ Key Takeaways

  • Use ...T only on the last parameter
  • Inside a variadic function, arguments are a slice of type []T
  • Use slice... to spread existing slices
  • append(a, b...) is idiomatic slice concatenation
  • For required-at-least-one APIs, use first T, rest ...T
practice & review