Skip to content

Beginner · Control flow and functions · topic 9 of 46 · 35 min

Variadic functions, function values and closures

Explanation

A variadic function accepts any number of arguments, like fmt.Println; inside, they arrive as a slice. Functions are values: you can store them in variables and pass them to other functions. A closure is a function that remembers variables from the place where it was created.

Variadic functions: any number of arguments

Sometimes you do not know how many values the caller will pass: total of 2 prices or of 20. Put ... before the type of the last parameter. Inside the function it is a slice of that type.

go
func total(prices ...int) int {
	sum := 0
	for _, p := range prices {
		sum += p
	}
	return sum
}

func main() {
	fmt.Println(total())             // 0
	fmt.Println(total(40, 25))       // 65
	cart := []int{120, 80, 300}
	fmt.Println(total(cart...))      // 500: spread a slice with ...
}
Only the last parameter can be variadic. Pass an existing slice with name... after it.

Functions are values

In Go a function can be stored in a variable, passed to another function and returned from one, just like an int. Its type is written like func(int) int: a function that takes an int and returns an int.

go
func apply(nums []int, f func(int) int) []int {
	out := make([]int, 0, len(nums))
	for _, n := range nums {
		out = append(out, f(n))
	}
	return out
}

func double(n int) int { return n * 2 }

func main() {
	fmt.Println(apply([]int{1, 2, 3}, double)) // [2 4 6]
	fmt.Println(apply([]int{1, 2, 3}, func(n int) int {
		return n * n
	})) // [1 4 9]
}
The second call passes an anonymous function: a function written inline without a name.

Closures: functions that remember

A closure is a function value that uses variables from outside its own body. It keeps those variables alive, even after the outer function has returned. Each closure created gets its own set of remembered variables.

go
func tokenCounter(prefix string) func() string {
	n := 0
	return func() string {
		n++
		return fmt.Sprintf("%s-%03d", prefix, n)
	}
}

func main() {
	next := tokenCounter("OPD")
	fmt.Println(next()) // OPD-001
	fmt.Println(next()) // OPD-002
	other := tokenCounter("LAB")
	fmt.Println(other()) // LAB-001
	fmt.Println(next())  // OPD-003
}
next and other each have their own n. %03d pads the number to 3 digits with zeros.
Think of it like this: A closure is like a hospital token machine at one counter. It remembers the last number it gave out. Another counter has its own machine with its own number. Pressing one does not change the other.

Where you will see these

  • sort.Slice and slices.SortFunc take a function that compares two items.
  • HTTP handlers are functions with the type func(http.ResponseWriter, *http.Request) (module 7).
  • Middleware returns a new handler that wraps another one, which is a closure (module 8).
  • go func() { ... }() starts an anonymous function as a goroutine (module 6).
go
people := []string{"Ravi", "Asha", "Zoya", "Bala"}
sort.Slice(people, func(i, j int) bool {
	return people[i] < people[j]
})
fmt.Println(people) // [Asha Bala Ravi Zoya]
The closure uses the people slice from outside to compare two positions.
Careful: Closures capture variables, not values. If the variable changes later, the closure sees the new value. Before Go 1.22 this caused the famous loop bug where every closure saw the final loop value; with go 1.22+ in go.mod, each loop round has its own variable.
Tip: Keep anonymous functions short. If one grows past about 10 lines, give it a name and make it a normal function: it becomes easier to read and to test.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Variadic functions, function values and closures · 1/7

More about functions

Let us look at three powerful function ideas: variadic functions, function values and closures.

Prefer a video by a teacher? Find free videos on “golang closures anonymous functions variadic functions” on YouTube ↗

In real life: Discount rules for a sale

An online store runs different discount rules during a festival sale. Each rule is a function value, so the store can switch rules from config without changing the checkout code. A closure builds "flat X percent off" rules for any percentage.

go
package main

import "fmt"

func percentOff(p int) func(int) int {
	return func(price int) int {
		return price - price*p/100
	}
}

func main() {
	rules := map[string]func(int) int{
		"diwali":  percentOff(20),
		"regular": percentOff(5),
	}
	price := 1500
	fmt.Println(rules["diwali"](price))  // 1200
	fmt.Println(rules["regular"](price)) // 1425
}
A map from rule name to function. percentOff returns a closure that remembers p.

Practice (0/4 done)

Task 1 · Question

func count(xs ...string) int { return len(xs) }. What is count("a", "b", "c")?

Task 2 · Question

What does this print? func counter() func() int { c := 0 return func() int { c++; return c } } ... a := counter() a() a() b := counter() fmt.Println(a(), b())

Task 3 · Question

nums := []int{1, 2}. Which call to func sum(xs ...int) int compiles?

Task 4 · Practice

Write func makeMultiplier(m int) func(int) int and use it to make triple and tenTimes. Then write func filter(nums []int, keep func(int) bool) []int and use it to keep only even numbers.

Quick quiz

Question 1

1.Inside func f(names ...string), what is the type of names?

Question 2

2.What is a closure?

Question 3

3.Which is a valid function type for "takes two ints and returns a bool"?

Question 4

4.Can a function have two variadic parameters?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

Inside func f(names ...string), what is the type of names?

Answer: []string

A variadic parameter is a slice inside the function.

What is a closure?

Answer: A function value that remembers variables from where it was created

A closure captures and keeps alive the outer variables it uses.

Which is a valid function type for "takes two ints and returns a bool"?

Answer: func(int, int) bool

Function types are written func(params) results.

Can a function have two variadic parameters?

Answer: No, only the last parameter can be variadic

Only the final parameter may use ...

A function calls its closure parameter many times, and the closure may change its captured state. Which bound fits?Related

Answer: FnMut

FnMut allows repeated calls that mutate state.

From Closures

What is a closure?Related

Answer: A function together with the outer variables it remembers

A closure is a function plus the variables from where it was created, which stay alive as long as the function does.

From Scope, hoisting and closures

What is a closure?Related

Answer: A function plus the variables from where it was created that it remembers

Closures keep access to the variables of the scope where the function was defined.

From Scope and closures

What is a closure?Related

Answer: A function that remembers variables from where it was created

Closures capture their surroundings.

From Functions

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 1 more topic in “Control flow and functions” before its exam: defer: clean up at the end.
  2. Module 2 exam: Control flow and functions · 13 questions · 20 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Collections and structs. 46 topics left in the course before the final exam.

← Previous: Functions and multiple return values