Last Updated: May 22, 2026
Some functions need to accept a flexible number of arguments. A cart total has to handle one item or fifty. A logger should accept any number of order IDs. Go's answer is variadic parameters, marked with ...T, which let a caller pass zero, one, or many values of the same type.
...T SyntaxA variadic parameter is declared by placing ... before the type. The function below sums a flexible list of prices:
The same function handled three, one, and zero arguments. Compare that to func sum(prices []float64) float64, where you'd have to write sum([]float64{}) to pass nothing. With ...float64, the empty call is natural.
Even though the caller writes sum(19.99, 4.50, 7.25) with separate arguments, inside the function prices is a regular []float64. You can index it, range over it, or hand it to any function that accepts a slice.
The %T verb confirms items is []string. The function reads items[0] and len(items) the same way it would for any slice, which is why variadic functions compose well with slice helpers, no conversion needed.
When the compiler sees sum(19.99, 4.50, 7.25), it allocates a new slice, copies each argument into it, and passes that slice to the function.
The slice is a fresh allocation, not shared with the caller's scope. Modifying it inside the function won't affect anything outside. If the variadic type is a slice or pointer (...[]int, ...*Customer), the elements still share their underlying data, same as any slice of references.
Cost: Each variadic call allocates a new backing array on the heap for the arguments (unless the compiler can prove it doesn't escape). In a hot loop, prefer building a []T once and spreading it, or accept []T directly.
...What if you already have a []float64 and want to pass it to sum? Writing sum(prices) doesn't compile, because prices is one value of type []float64 while sum expects individual float64 arguments. The fix is the spread operator, the same ... token used at the call site:
cart... means "expand this slice as if each element were a separate argument". The function sees the same prices ...float64 parameter, and you skipped writing a forwarding loop.
Two rules go with the spread. The slice's element type must match the variadic type: you can spread []float64 into ...float64, but not into ...int. And you can't mix spreads with regular arguments. This won't compile:
The error is too many arguments in call to sum. The spread is all-or-nothing: either pass individual values, or pass exactly one spread expression. If you need to combine a fixed value with a slice, build the full slice first:
The append([]float64{shipping}, cart...) call builds a new slice with shipping at the front, then spreads that into sum. A little verbose, but explicit about what's happening.
A function can have at most one variadic parameter, and it must sit at the end of the parameter list. The compiler needs to know unambiguously where the regular parameters stop and the variable-length pack begins.
The fixed customerID comes first, then any number of item names. Reversing the order, func logOrder(items ...string, customerID int), fails with can only use ... with final parameter. The same rule rules out having two variadic parameters; if you need that, accept two slices.
fmt.Println Accepts AnythingYou've been calling fmt.Println since lesson one. Here's its signature from the standard library:
a ...any is a variadic parameter whose type is any (an alias for interface{}). Inside the function, a is a []any, a slice that can hold values of any concrete type. That's why fmt.Println("order:", 42, true, 3.14) accepts strings, ints, bools, and floats in the same call.
The same pattern shows up in fmt.Printf, log.Printf, and append. append(s, x, y, z) and append(s, more...) are both legal because append is variadic in its tail.
The function below applies a stack of discount codes to a cart total, where each code is one of a known set.
One function handled four call shapes: no codes, one code, several codes, and a pre-built slice spread with .... This flexibility is the point of the variadic form.