SoFunction
Updated on 2025-03-04

A brief analysis of the creation and use of closures in Golang

Closures are a powerful feature of programming languages ​​including Go. With closures, you can encapsulate data in a function and access it through the return value of the function. In this article, we will cover the basics of closures in Go, including what they are, how they work, and how to use them effectively.

What is a closure

There is an official explanation from go:

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

Translated:

Function literals (anonymous functions) are closures: they can refer to variables defined in surrounding functions. These variables are then shared between the surrounding function and function literals, and they continue to exist as long as they are still accessible.

A closure is a way of creating functions that can access variables defined outside of their bodies. A closure is a function that can capture the state of its surroundings. This means that a function can access variables that are not defined in its parameter list or in its body.Closure functions can access these variables after external functions return

Create closures in Go

In Go, you can create closures using anonymous functions. When a closure is created, the function captures the state of its surroundings, including any variables defined in the external function. Closure functions can access these variables after the external function returns.

Here is an example of creating a closure in Go:

func adder() func(int) int { // External functions	sum := 0
	return func(x int) int { // Internal functions		("func sum: ", sum)
		sum += x
		return sum
	}
}

func main() {
	a := adder()
	(a(1))
	(a(2))
	(a(3))
}

In this example, we define an adder function that returns an anonymous function. Anonymous function captures the defined in the adder functionsumThe state of the variable. Each time an anonymous function is called, it adds the parameters to the sum variable and returns the result.

So its output is:

func sum:  0
1
func sum:  1
3
func sum:  3
6

Using closures in Go

In Go, closures can be used for a variety of purposes, including encapsulating data with functions, creating generators, iterators, and memoization functions.

Here is an example of using closures to encapsulate data with functions:

func makeGreeter(greeting string) func(string) string {
	return func(name string) string {
		("func greeting: %s, name: %s\n", greeting, name)
		return greeting + ", " + name
	}
}

func main() {
	englishGreeter := makeGreeter("Hello")
	spanishGreeter := makeGreeter("Hola")

	(englishGreeter("John"))
	(englishGreeter("Tim"))
	(spanishGreeter("Juan"))
	(spanishGreeter("Taylor"))
}

In this case, we define a name calledmakeGreeterfunction, which returns an anonymous function. The anonymous function takes a string parameter and returns a string that concatenates the greeting and name. We created two greeting programs, one for English and one for Spanish, and then called them with different names.

So its output is:

func greeting: Hello, name: John
Hello, John
func greeting: Hello, name: Tim
Hello, Tim
func greeting: Hola, name: Juan
Hola, Juan
func greeting: Hola, name: Taylor
Hola, Taylor

Replace captured variables

One of the powerful features of Go closures is the ability to change captured variables. This makes the behavior in the code more flexible and dynamic. Here is an example:

func makeCounter() func() int {
	i := 0
	return func() int {
		("func i: ", i)
		i++
		return i
	}
}

func main() {
	counter := makeCounter()
	(counter())
	(counter())
	(counter())
}

In this case,makeCounterThe function returns a closure, and each call increments the counter. The i variable is captured by the closure and can be modified to update the counter.

So its output is:

func i:  0
1
func i:  1
2
func i:  2
3

Escape variables

Another advanced concept of Go closures is variable escape analysis. In Go, variables are usually allocated on the stack and de-allocated when out of scope. However,When a variable is captured by a closure, it must be allocated on the heapto ensure that it can be accessed after the function returns. This leads to performance overhead, so it is important to understand when and how variables escape.

Let's compare two methods:

func makeAdder1(x1 int) func(int) int {
	return func(y1 int) int {
		return x1 + y1
	}
}

func makeAdder2(x2 int) func(int) int {
	(x2)
	return func(y2 int) int {
		return x2 + y2
	}
}

func main() {
	a := makeAdder1(5)
	(a(1))

	b := makeAdder2(6)
	(b(1))
}

makeAdder1andmakeAdder2The difference is in the functionxWhether it is used.

And we analyze through escape:

go build -gcflags "-m" 

The following output will be obtained:

./:5:6: can inline makeAdder1
./:6:9: can inline makeAdder1.func1
./:13:9: can inline makeAdder2.func1
./:12:13: inlining call to
./:19:17: inlining call to makeAdder1
./:6:9: can inline main.makeAdder1.func1
./:20:15: inlining call to main.makeAdder1.func1
./:20:13: inlining call to
./:23:13: inlining call to
./:6:9: func literal escapes to heap
./:12:13: ... argument does not escape
./:12:14: x2 escapes to heap
./:13:9: func literal escapes to heap
./:19:17: func literal does not escape
./:20:13: ... argument does not escape
./:20:15: ~R0 escapes to heap
./:23:13: ... argument does not escape
./:23:15: b(1) escapes to heap

Judging from the escape analysis results,xVariables are captured by closures and must be allocated on the heap. However, ifxVariables are not used by any other code other than closures, and the compiler can optimize and assign them to the stack.

Shared closure

Finally, closures in Go can be shared among multiple functions, enabling greater flexibility and modular code. Here is an example:

type Calculator struct {
	add func(int, int) int
}

func NewCalculator() *Calculator {
	c := &Calculator{}
	 = func(x, y int) int {
		("func x: %d, y: %d\n", x, y)
		return x + y
	}
	return c
}

func (c *Calculator) Add(x, y int) int {
	return (x, y)
}

func main() {
	calc := NewCalculator()
	((1, 2))
	((2, 3))
}

In this case,CalculatorThe structure has aaddThe function, which is inNewCalculatorThe function is initialized by closure.CalculatorStructuralAddThe method only needs to be calledaddfunction, so that it can be reused in multiple contexts.

So its output is:

func x: 1, y: 2
3
func x: 2, y: 3
5

in conclusion

In Go programming, closures are a powerful tool that can be used to encapsulate data with functions, create generators and iterators, etc. They provide a way to access variables defined in a function in vitro, even after the function returns.

This is the article about briefly analyzing the creation and use of closures in Golang. For more related go closure content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!