Go Closure

In this tutorial, you will learn about closures in Go programming with the help of examples.

Go closure is a nested function that allows us to access variables of the outer function even after the outer function is closed.

Before we learn about closure, let's first revise the following concepts:

  • Nested Functions
  • Returning a function

Nested function in Golang

In Go, we can create a function inside another function. This is known as a nested function. For example,

package main
import "fmt"

// outer function
func greet(name string) {

// inner function var displayName = func() { fmt.Println("Hi", name) }
// calling inner function displayName() } func main() { // calling outer function greet("John") // Hi John }

In the above example, we have created an anonymous function inside the greet() function.

Here, var displayName = func() {...} is a nested function. The nested function works similar to the normal function. It executes when displayName() is called inside the function greet().


Returning a function in Go

We can create a function that returns an anonymous function. For example,

package main
import "fmt"

func greet() func() {

return func() { fmt.Println("Hi John") }
} func main() { g1 := greet() g1() }

Output

Hi John

In the above example, we have created the greet() function.

func greet() func() {...}

Here, func() before the curly braces indicates that the function returns another function.

Also, if you look into the return statement of this function, we can see the function is returning a function.

From main(), we call the greet() function.

g1 := greet()

Here, the returned function is now assigned to the g1 variable. Hence, g1() executes the nested anonymous function.


Go Closures

As we have already discussed, closure is a nested function that helps us access the outer function's variables even after the outer function is closed. Let's see an example.

package main
import "fmt"

// outer function
func greet() func() string {

  // variable defined outside the inner function
  name := "John"
  
// return a nested anonymous function return func() string { name = "Hi " + name return name }
} func main() { // call the outer function message := greet()
// call the inner function fmt.Println(message())
}

Output

Hi John

In the above example, we have created a function named greet() that returns a nested anonymous function.

Here, when we call the function from main().

message := greet()

The returned function is now assigned to the message variable.

At this point, the execution of the outer function is completed, so the name variable should be destroyed. However, when we call the anonymous function using

fmt.Println(message())

we are able to access the name variable of the outer function.

It's possible because the nested function now acts as a closure that closes the outer scope variable within its scope even after the outer function is executed.

Let's see one more example to make this concept clear for you.


Example: Print Odd Numbers using Golang Closure

package main
import "fmt"

func calculate() func() int {
  num := 1

// returns inner function return func() int { num = num + 2 return num }
} func main() { // call the outer function odd := calculate()
// call the inner function fmt.Println(odd())
fmt.Println(odd()) fmt.Println(odd()) // call the outer function again odd2 := calculate()
fmt.Println(odd2())
}

Output

3
5
7
3

In the above example,

odd := calculate()

This code executes the outer function calculate() and returns a closure to the odd number. That's why we can access the num variable of calculate() even after completing the outer function.

Again, when we call the outer function using

odd2 := calculate()

a new closure is returned. Hence, we get 3 again when we call odd2().


Closure helps in Data Isolation

As we see in the previous example, a new closure is returned every time we call the outer function. Here, each returned closure is independent of one another, and the change in one won't affect the other.

This helps us to work with multiple data in isolation from one another. Let's see an example.

package main
import "fmt"

func displayNumbers() func() int {
  number := 0

// inner function return func() int { number++ return number }
} func main() { // returns a closure num1 := displayNumbers() fmt.Println(num1()) fmt.Println(num1()) fmt.Println(num1()) // returns a new closure num2 := displayNumbers() fmt.Println(num2()) fmt.Println(num2()) }

Output

1
2
3
1
2

In the above example, the displayNumbers() function returns an anonymous function that increases the number by 1.

return func() int {
  number++
  return number
}

Inside the main() function, we assign the function call to num1 and num2 variables.

Here, we first call the closure function using num1(). In this case, we get outputs 1, 2, and 3.

Again, we call it using num2(). In this case, the value of the number variable doesn't start from 3; instead, it starts from 1 again.

This shows that the closure returned from displayNumbers() is isolated from one another.

Did you find this article helpful?