My Go courses are discounted for the next few weeks to help out anyone who may need or want access to them. I'm also going to try to help out anyone who can't afford a course, and I will be writing posts about working from home over the next week in an attempt to help anyone new to WFH. Read more here .

What is a Closure?

In a previous article on testing with Go there is an example that uses a closure to create table-driven tests. This is a fairly common practice in Go, but after writing the article a developer reached out to me and made the following comment.

“I learned something. Mainly that I have no idea how Go closures work”

Ouch! That means that despite the rest of the article being packed with great content on testing, if a reader didn’t understand closures they weren’t going to get the full benefit of the article.

This post is intended to fix that problem. In it I we are going to discuss what closures are and why you should care about them. Then in a followup article, 5 Useful Ways to Use Closures in Go (redundant, I know), we will explore some of the more common use cases for closures to help give you ideas of where and when to use closures in your own code. Each use case has a specific example that I have encountered, so they aren’t just imaginary uses.

Anonymous functions

Before we jump into closures, we need to first talk about anonymous functions. An anonymous function is the same as a regular old function, but it doesn’t have a name - hence the term “anonymous”. Instead, an anonymous function is created dynamically, much like a variable is.

Like most things, this can be easier to explain with code. Below is what a normal function would look like.

func DoStuff () { // Do stuff }

If we wanted to turn this same function into an anonymous function, we wouldn’t declare it quite the same way; rather than starting with the func keyword, we could instead create a variable that has a func() type. After doing this, we could create and assign an anonymous function to he variable.

var DoStuff func () = func () { // Do stuff }

There are a lot of subtle differences between these two, but for most practical purposes, the big difference is that we could assign a new function to the DoStuff variable at runtime, allowing us to dynamically change what DoStuff() does.

package main import "fmt" var DoStuff func () = func () { // Do stuff } func main () { DoStuff () DoStuff = func () { fmt . Println ( "Doing stuff!" ) } DoStuff () DoStuff = func () { fmt . Println ( "Doing other stuff." ) } DoStuff () }

If you run this program you would see the following output.

Doing stuff ! Doing other stuff .

We are seeing two different lines of output because we declared three different anonymous functions, and the last two each are printing out something different.

Anonymous functions can accept parameters, return data, and do pretty much anything else a normal function can do. You can even assign a regular function to a variable just like you do with an anonymous function.

package main import "fmt" var DoStuff func () = func () { // Do stuff } func RegFunc () { fmt . Println ( "reg func" ) } func main () { DoStuff () DoStuff = RegFunc DoStuff () }

The only real difference between a regular function and an anonymous one is that anonymous functions aren’t declared at a package level. They are declared more dynamically and are typically either used and then forgotten or are assigned to a variable for later use.

Closures

A closure is a special type of anonymous function that references variables declared outside of the function itself. That means that like in the previous examples, we will be creating a function dynamically, but in this case we will be using variables that weren’t passed into the function as a parameter, but instead were available when the function was declared.

This is very similar to how a regular function can reference global variables. You aren’t directly passing these variables into the function as a parameter, but the function has access to them when it is called.

Let’s take a look at a closure in action. In this next example we are going to create a closure that keeps track of how many times it has been called, and returns that number.

package main import "fmt" func main () { n := 0 counter := func () int { n += 1 return n } fmt . Println ( counter ()) fmt . Println ( counter ()) }

If you were to run this code you would get the output:

1 2

Notice how our anonymous function has access to the n variable, but this was never passed in as a parameter when counter() was called. This is what makes it a closure!

Closures provide data isolation

One problem with the previous example is a problem that can also pop up when using global variables. Any code inside of the main() function has access to n , so it is possible to increment the counter without actually calling counter() . That isn’t what we want; Instead we would rather have n isolated so that no other code has access to it.

To do this we need to look at another interesting aspect of closures, which is the fact that they can still reference variables that they had access to during creation even if those variables are no longer referenced elsewhere.

package main import "fmt" func main () { counter := newCounter () fmt . Println ( counter ()) fmt . Println ( counter ()) } func newCounter () func () int { n := 0 return func () int { n += 1 return n } }

In this example our closure references the variable n even after the newCounter() function has finished running. This means that our closure has access to a variable that keeps track of how many times it has been called, but no other code outside of the newCounter() function has access to this variable. This is one of the many benefits of a closure - we can persist data between function calls while also isolating the data from other code.

Want to improve your Go skills? Are you looking to practice Go, but can’t think of a good project to work on? Or maybe you just don’t know what techniques and skills you need to learn next, so you don't know where to start. If so, don’t worry - I’ve got you covered! Gophercises is a FREE course where we work on exercise problems that are each designed to teach you a different aspect of Go. This includes topics ranging from basic string manipulation all the way to more advanced topics like functional options and concurrency. Each exercise has a sample solution, as well as a screencast (video) where I code the solution while walking you through the code. Plus, the Gophers are really cute 😉 FREE Course Gophercises - Exercises for Budding Gophers

Up Next…

In my next post - - I go over several common use cases for closures, including ways that closures can make code easier to read and understand, how closures can prevent the callback hell that rears its head so often in javascript, and even how you can use closures to create middleware that times your web applications execution time. You should check it out so you can easily recognize situations where closures can be helpful!

Following that I will have a small another article dedicated to gotchas and common mistakes that developers make when creating closures, and how to avoid them.

This article is part of the series, Closures in Go. Next Article