You don't know Closure


🔐 What is a Closure in Go?
A closure is a function value that remembers the variables from the scope in which it was created.
✅ In simple terms:
A closure "closes over" its environment and keeps those variable values alive even after the outer function exits.
🔧 Basic Syntax
func outer() func() {
x := 10
return func() {
fmt.Println(x)
}
}
Here:
The inner function
func() { fmt.Println(x) }
is a closure.It captures the variable
x
from its outer scope.
⚠️ Common Confusion: Closure vs Normal Function
A normal function doesn’t retain local variables after returning.
A closure retains state from the outer function.
A visual diagram of closures:
💼 Real-Life Use Cases
Use Case | How Closures Help |
Generators | Return a function that tracks state |
Event handlers | Wrap logic that depends on context |
Custom sort logic | Pass behavior into functions |
Encapsulation | Hide variables from outer world |
Go closures capture variables from outer scopes. Let's explain exactly what happens step by step, including what is captured and how Go handles it.
const a int = 10 // constant (compile-time known)
var p int = 20 // global variable (heap)
func outer() func() {
var money int = 100 // local variable (captured)
var age int = 30 // local variable (not used in closure)
show := func() {
money = money + a + p // uses money (local), a (const), p (global)
fmt.Println(money)
}
return show
}
🧪 What Gets Captured and How?
Variable | Type | Captured by Closure? | How it's handled in Go |
a | Constant | ✅ Yes | Inlined at compile time |
p | Global | ✅ Yes | Captured as reference |
money | Local | ✅ Yes | Stored in closure's heap |
age | Local | ❌ No (not used) | Optimized away |
🔬 Explanation of Behavior
a
(constant):Constants in Go are inlined at compile time.
So
a
is treated as a literal10
.
p
(global variable):p
is declared outside the function.It is captured by reference, so any changes to
p
Outside will affect closure behavior (if accessed).
money
(local variable):Local to
outer()
.Since it's used in the closure, Go allocates it on the heap, not the stack.
This allows the inner function to access it even after
outer()
it has returned.
age
(unused variable):- It's not used in the closure, so Go will optimize it away (won’t be captured or retained).
🔁 Here is a Problem:
“If
show()
is defined insideouter()
, andouter()
has already returned, then how can I still callshow()
later, whenouter()
’s stack frame is gone?”
This is a valid question — in most languages, local variables are destroyed when the function returns.
🧠 But here's the magic: Closures change that!
🔓 Go’s Solution: Escape Analysis + Heap Allocation
Go uses a mechanism called escape analysis during compilation.
If a local variable is accessed by a closure, Go moves that variable from the stack to the heap instead of destroying it with the function’s stack frame.
📦 What Happens Internally
Let’s look at this code again:
func outer() func() {
var money int = 100
show := func() {
money += 10
fmt.Println(money)
}
return show
}
🔬 Internally:
money
is declared insideouter()
(normally stack).show
referencesmoney
, andshow
is returned.Go detects that
money
is used outsideouter()
— So it movesmoney
to the heap.show()
retains a pointer tomoney
it on the heap.When you call
show()
, it still has accessmoney
via that pointer.
💡 Analogy
Imagine outer()
writes a note (a variable like money
) and leaves it on a public notice board (the heap). Even after outer()
the leaves (stack is cleared), anyone with a reference (like show
) can still read and update that note.
💬 Closures extend the lifetime of captured variables beyond their original stack frame — by allocating them on the heap.
You can even see this decision by running:
go build -gcflags="-m" yourfile.go
📦 Is Heap Allocation Always the Default for Closures?
Not always, but for most practical closures — yes. 👉 If the variable:
is used by an inner function
And the inner function outlives the outer one
→ It must be moved to the heap
🧠 Let's clarify a doubt
What happened If
outer()
returns a primitive value (likeint
), it behaves like a normal function or formed a closure ?
🧪 Answer 1: No Closure Needed for primitive return.
func outer() int {
x := 5
return x // 👈 value is copied, stack-safe
}
x
is stored on the stack.Returned as a value → copied.
No closure.
❌ Nothing escapes → no heap allocation.
🧪 Answer 2: Closure Needed if the Function returns using the outer variable.
func outer() func() {
x := 5
return func() {
fmt.Println(x) // 👈 closure: captures `x`
}
}
Now the returned function uses
x
.x
must survive after outer() exits.So, Go moves
x
to the heap.The inner function holds a pointer to x.
✅ This is the default behavior when closures are formed.
🧠 Let's summarize the Closure Behavior, Go closures are not magic — they’re syntactic sugar over structs + function pointers. Any captured variable that escapes the function goes to the heap. Escape analysis + garbage collector handles it efficiently. Thank you for reading this article.
Subscribe to my newsletter
Read articles from AL Hasib directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
