← Back to Go Course | Chapter 4: Functions | Lesson 6 of 7

Closures

A closure is a function that packs its own little backpack of variables from where it was created, so it can still use them later even after it travels somewhere else.
Syntax
go
func outer() func(param type) returnType {
    captured := value
    return func(param type) returnType {
        // uses captured
    }
}

What Makes a Closure

A closure is simply a function literal that refers to a variable declared outside of it -- Go automatically keeps that variable alive and accessible to the function for as long as the closure itself exists.

Example: What Makes a Closure

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// main is where the program starts running
func main() {
	// Short declaration: create message and infer the type
	message := "hello"
	// Short declaration: create greet and infer the type
	greet := func() {
		// Print the values followed by a newline
		fmt.Println(message)
	}
	greet()
}

Closures Share, Not Copy, Captured Variables

Because a closure captures the variable itself rather than a snapshot of its value, changes made inside the closure are visible outside it, and changes made outside are visible the next time the closure runs.

Example: Closures Share, Not Copy, Captured Variables

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// main is where the program starts running
func main() {
	// Short declaration: create count and infer the type
	count := 0
	// Short declaration: create increment and infer the type
	increment := func() {
		count++
	}
	increment()
	increment()
	// Print the values followed by a newline
	fmt.Println("count:", count)
}

Building a Counter Factory

A classic use of closures is a function that returns another function, which keeps its own private state alive between calls -- like a counter generator that hands back an independent, incrementing counter each time it's called.

Note: Each call to makeCounter creates a brand-new, independent count variable.

Example: Building a Counter Factory

markup
// Every Go file belongs to a package; main builds an executable
package main

// Import the fmt package
import "fmt"

// Define the function makeCounter
func makeCounter() func() int {
	// Short declaration: create count and infer the type
	count := 0
	// Send func() int { back to the caller
	return func() int {
		count++
		return count
	}
}

// main is where the program starts running
func main() {
	// Short declaration: create counter and infer the type
	counter := makeCounter()
	// Print the values followed by a newline
	fmt.Println(counter())
	fmt.Println(counter())
	fmt.Println(counter())
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Assuming each closure gets its own independent copy of a captured variable, when closures actually share a reference to the same variable.
  2. Creating closures in a loop pre-Go-1.22 and being surprised they all captured the same final loop variable value.
  3. Using a closure to mutate shared state from multiple goroutines without any synchronization, causing a data race.
Chapter Summary
  • A closure is a function literal that references variables from outside its own body.
  • Captured variables are shared by reference, not copied -- changes inside the closure are visible outside, and vice versa.
  • Closures are commonly used to build function factories, like counters or accumulators.
  • Since Go 1.22, each loop iteration gets a fresh variable, making closures inside loops behave intuitively.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.