CodeNewbie Community 🌱

Cover image for Golang: Closures
Meet Rajesh Gor
Meet Rajesh Gor

Posted on • Originally published at meetgor.com

Golang: Closures

#go

Introduction

In the previous part of the series, we covered anonymous functions and in this section, we will look into closures which are quite a cool concept for various things. Closures are basically a function that returns a function instead of a value, so basically we will leverage anonymous functions for creating closures.

Simple Closures

A simple closure can be constructed for understanding how we can use closures in golang. We will return a function from a function, that is a simple closure. So, in the below code example, we have created a function gophy() which takes no parameters but returns a function that returns a string. The function simply returns an anonymous function that returns a string.

We will initialize the variable g that is assigned to the function gophy which will simply return a function call. We are not calling the function simply returning the call to the function gophy that has the return value as the anonymous function. We will simply have the function in the variable g rather than the simple value string. So we will have to call the g variable for actually returning the string.

package main

import "fmt"

func gophy() func() string{
  return func() string{
    return "Hello, Gophers!"
  }
}

func main() {

    // using clousure/anonymous function to return a value
    // that value can be assigned to the variable
    g := gophy()
    fmt.Println(g())
}

Enter fullscreen mode Exit fullscreen mode
$ go run simple.go
Hello, Gophers!

Enter fullscreen mode Exit fullscreen mode

So, that is how we can call the function g that will return a string, so we have the function body stored in the variable g. We can call it as many times as we want.

Variable Scope in Closures

We can even use variables that will remain in the function scope once it is initialized. So, let’s say we have a function that will increment the counter, but if we want to keep the counter the same throughout the program, we might have to use a global variable so as to maintain the context, but with closures, we will retain the value once we have initialized the function call.

In the below example, we are creating the function incrementer that is a closure with int as the return type. We are initializing the variable counter that will be acting as the counter in the program, the function returns an anonymous function that will increment the counter and return it.

Here, when we create an instance of the increment function it basically initializes the counter to 0 and returns the anonymous function as a call. Now, c will act as a function that has the counter variable bound to it and we can call c that will, in turn, call the anonymous function keeping the scope of the counter variable. So, each time we call the function c it will increment the counter and thus we keep the counter inside the scope of the function incrementer in the c variable.

package main

import "fmt"

func inrementer() func() int{
  counter := 0
  return func() int{
    counter += 1
    return counter
  }
}

func main() {

  c := inrementer()

  fmt.Println(c())
  fmt.Println(c())
  fmt.Println(c())
  fmt.Println(c())
  fmt.Println(c())

}

Enter fullscreen mode Exit fullscreen mode
$go run simple.go
1
2
3
4
5

Enter fullscreen mode Exit fullscreen mode

If we want to extend the functionality, we can even assign the function call c() to a variable and access the returned value which will be the current state of the counter.

We can even use different scope or closures tied to a particular function, that is we can bind data to a different instances of a closure.

package main

import "fmt"

func inrementer() func() int{
  counter := 0
  return func() int{
    counter += 1
    return counter
  }
}

func main() {

  c1 := inrementer()

  fmt.Println(c1())
  fmt.Println(c1())
  fmt.Println(c1())

  c2 := inrementer()

  fmt.Println(c2())
  fmt.Println(c2())
  fmt.Println(c2())
  fmt.Println(c2())

}

Enter fullscreen mode Exit fullscreen mode
$go run simple.go
1
2
3
1
2
3
4

Enter fullscreen mode Exit fullscreen mode

Here we have c1 and c2 forming different closures and thereby we can have different scopes of the variables associated with it. The variable is bound to the instance it which was initialized, so we can see the different closure instances having different values.

Factorial of a function with Closures

We can create some interesting programs with closures, we will implement the calculation of factorial with closures in golang.

This will be a factorial function that returns an anonymous function with the return type as int. The function will initialize the variable fact which will store the actual factorial value and n as the initial number for calculating the factorial of it.

Inside the anonymous function, we will calculate the factorial and increment the number and simply return the factorial value from the function. The fact variable will contain the factorial of the number n, so here we can leverage the use of closures as we will maintain the state of the variable fact and n from the previous calls or the initialization of the function.

Inside the main function, we have created the f variable and called the factorial function, so that will initialize the fact and n of the variable and thereby returning the anonymous function call. Now we can call the variable f as many times as we want that will simply return the factorial of the number incremented each time we call the function.

package main

import "fmt"

func factorial() func() int{
    fact, n := 1, 1
    return func() int{
    fact = fact * n
    n += 1
        return fact
    }
}

func main() {

  f := factorial()
  fmt.Println(f())
  fmt.Println(f())
  fmt.Println(f())
  fmt.Println(f())
  fmt.Println(f())

}

Enter fullscreen mode Exit fullscreen mode
$ go run simple.go
1
2
6
24
120

Enter fullscreen mode Exit fullscreen mode

So, we can see that the factorial is getting printed for each call and the number is being incremented at each call.

So that's the basics of closures in golang, we can use closures to keep the content secured and encapsulated from different function calls. We can bind data with closures, with the help of anonymous functions a closure can be constructed and data can be bound to a particular function called scope.

That's it from this part. Reference for all the code examples and commands can be found in the 100 days of Golang GitHub repository.

Conclusion

From this post, we could understand the fundamentals of closures in golang. The basic concept of closures in golang was understood with a few examples. Thank you for reading, if you have any queries or feedback please leave them in the comments or on my social handles. Happy Coding :)

Top comments (0)