Menu

Golang Variadic Functions: ...T Params and s... Spreading

A variadic Go function accepts any number of trailing arguments of one type. Learn the ...T syntax, how to pass a slice with s..., the aliasing gotcha, and how fmt.Println uses ...any.

This page includes runnable editors - edit, run, and see output instantly.

The ...T parameter

Put ... before the type of the last parameter. Callers can then pass any number of arguments of that type, including none. Inside the function the parameter is a slice.

This prints 0, 5 and 10. With no arguments, nums is a nil slice with length 0, so the loop simply does not run.

Regular parameters can come first. Only the last one may be variadic:

func logf(level string, format string, args ...any)

Passing a slice with s...

If you already have a slice, you cannot pass it directly: sum(nums) fails with cannot use nums (variable of type []int) as int value in argument to sum. Spread it with ... after the argument:

append is variadic too, which is why joining two slices is append(a, b...).

The spread must be the only thing in the variadic position. sum(1, nums...) fails with too many arguments in call to sum. Build the combined slice first with append.

The slice is shared, not copied

When you call with individual arguments, Go creates a fresh slice for them. When you spread an existing slice with s..., the function receives that same slice. Writes to its elements change the caller's data.

Output:

[7 8 9]
[0 8 9]

A variadic function that modifies its parameter should document that, or copy first with slices.Clone. Most variadic functions only read their arguments, so this rarely bites, but it is a real difference from languages where rest parameters are always a fresh array.

...any and how fmt.Println works

fmt.Println is declared as func Println(a ...any) (n int, err error). any (an alias for interface{} since Go 1.18) accepts every type, so you can pass strings, numbers and structs in one call.

Forwarding is the important detail: logf passes args... to Printf. Without the dots, Printf receives one argument, a []any, and the warning line prints [WARN] [3 8] of %!d(MISSING) workers idle. go vet reports missing ... in args forwarded to printf-like function only when the format parameter is passed through unchanged, as in fmt.Printf(format, args). Here the format is built with +, so vet stays silent and the dots are on you.

A []string cannot be spread into ...any. The element types must match exactly, so convert first:

names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
	args[i] = n
}
fmt.Println(args...)

Requiring at least one argument

A variadic parameter accepts zero arguments. When the function needs at least one, make the first a regular parameter:

Now maxOf() is a compile error instead of a runtime check. The built-in max itself works the same way: it requires at least one argument.

Variadic parameters as optional arguments

Go has no default parameter values. A variadic parameter is sometimes used to fake one optional argument:

func connect(addr string, timeout ...time.Duration)

This works but reads poorly, since callers can pass three timeouts and the signature does not say which one wins. For more than one option, prefer a config struct, or the functional options pattern (...Option, where each Option is a function that sets a field). That pattern is variadic, but every argument has a clear meaning.

Rules Reference

RuleExample
Only the last parameter can be variadicfunc f(a string, b ...int)
Inside, the parameter is a sliceb has type []int
Zero arguments give a nil slicef("x") makes b == nil
Spread a slice with ...f("x", nums...)
Spread shares the backing arraywrites to b[i] change nums
No mixing spread with extra valuesf("x", 1, nums...) does not compile

Frequently Asked Questions

What is a variadic function in Go?

A function whose last parameter has the form ...T, like func sum(nums ...int) int. It can be called with zero or more arguments of type T, and inside the function the parameter is a []T slice.

How do I pass a slice to a variadic function in Go?

Add ... after the slice: sum(nums...). The slice is passed as is, without copying, so the function sees the same backing array. You cannot mix spread and individual arguments: sum(1, nums...) does not compile.

Does Go have a spread operator?

Only for the last argument of a variadic call: f(s...). There is no general spread for building arrays, structs, or calling non-variadic functions with a slice. append(a, b...) is the same rule applied to the built-in append.

Can a Go function have two variadic parameters?

No. Only the final parameter can be variadic, and there can be one per function. Pass extra lists as ordinary slice parameters.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED