#An Introduction to Go Closures - Tutorial

Elliot Forbes Elliot Forbes · Jun 2, 2026 · 4 min read

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.

main.go
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:

$ go run main.go
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:

main.go
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:

$ go run main.go
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: