#An Introduction to Go Closures - Tutorial
In this tutorial, we are going to look at closures in Go. We’ll cover the theory behind them, then see how you can use them in your own Go applications.
Closures - The Theory
So, let’s dive into the theory.
We can create and use closures in any programming language that supports functions as first-class objects. Go happens to be one such language, otherwise this article would be pointless.
The technical definition of a closure, from Wikipedia, is “a technique for implementing a lexically scoped name binding in a language with first-class functions”.
Don’t worry — when I first read that, I scratched my head a little and had to think it through.
In layman’s terms, a closure is a function value that can reference variables defined outside its own body.
Note - It’s important to note the distinct differences between both closures and anonymous functions which are commonly mistaken for closures. You can learn more about anonymous functions here: Go Anonymous Functions
A Simple Go Closure Example
Let’s create a really simple example of a closure that will hopefully clarify how it works.
We’ll start with a function called getLimit(), which returns the closure in
this example.
It declares a limit variable of type int, set to 10. The returned function
decrements limit by one and returns it, so each call hands back a smaller
value.
package main
import "fmt"
func getLimit() func() int {
limit := 10
return func() int {
limit -= 1
return limit
}
}
func main() {
limit := getLimit()
fmt.Println(limit())
fmt.Println(limit())
}
Now, if we run this, we should see the following output:
9
8
But why does this matter? The limit variable is bound to the specific closure
returned by getLimit(). If we bind a second closure to limit2, it gets its
own independent copy of limit with its own state:
package main
import "fmt"
func getLimit() func() int {
limit := 10
return func() int {
limit -= 1
return limit
}
}
func main() {
limit := getLimit()
fmt.Println(limit()) // 9
fmt.Println(limit()) // 8
limit2 := getLimit()
fmt.Println(limit2()) // 9
fmt.Println(limit2()) // 8
fmt.Println(limit()) // 7
}
When we run this, we should see the following output:
9
8
9
8
7
Notice how limit and limit2 count down independently. Each closure carries
its own captured copy of limit — that is the key behaviour of a closure in Go.
Conclusion
In this tutorial, we covered the basic theory of closures and how you can use them in your own Go programs.
Hopefully you found this tutorial useful. If you have any feedback or suggestions, I would love to hear them in the comments section below!
Note - If you want to keep track of when new Go articles are posted to the site, then please feel free to follow me on twitter for all the latest news: @Elliot_F.
Frequently Asked Questions
What is a closure in Go?
A closure is a function value that references variables defined outside its own body. In Go, this works because functions are first-class objects that can be returned from other functions and assigned to variables.
How do closures capture variables in Go?
Each closure keeps a reference to the variables in its enclosing scope. When you create two closures from the same function, each one captures its own copy of those variables and maintains independent state.
What is the difference between a closure and an anonymous function in Go?
An anonymous function is simply a function without a name. It becomes a closure only when it references variables from the surrounding scope. Not every anonymous function is a closure.
Why are closures useful in Go?
Closures let you bind state to a function without using a global variable or a struct. This is handy for generators, counters, and configuration helpers that need to remember a value between calls.
What’s Next
Now that you understand closures, these related Go tutorials build on the same ideas around first-class functions:
Continue Learning
Structured Logging in Go with log/slog - The Complete Guide
Learn structured logging in Go with the standard library log/slog package - handlers, levels, context, custom handlers, and why it replaces logrus, zap and zerolog.
Makefiles for Go Developers
Learn how to use Makefiles in Go projects to automate builds, cross-compilation, and common dev tasks with a single `make` command.
Building a Web Server in Go with net/http
Learn how to build a web server in Go using the net/http package — covering handlers, static files, and HTTPS in under 100 lines of code.
Creating a RESTful API With Golang
this tutorial demonstrates how you can create your own simple RESTful JSON api using Go(Lang)