Module lessons (4/5)
Closures and functions as values
In Go, functions are first-class values: they can be assigned to variables, passed to other functions, and returned. A closure is a function literal (anonymous) that captures the variables of the environment in which it is defined.
Function literal
A function with no name, written inline. You call it immediately or assign it:
greet := func(name string) {
fmt.Println("ciao", name)
}
greet("Anna")
// invocata immediatamente:
func() { fmt.Println("subito") }()The type of greet is func(string).
Capturing the environment: closure
The function literal can read and modify the variables visible in its lexical scope, even after the outer function has returned:
func counter() func() int {
n := 0
return func() int {
n++
return n
}
}
c := counter()
fmt.Println(c(), c(), c()) // 1 2 3n "lives" as long as at least one closure references it. Each call to
counter() creates a new, independent n.
Closure as callback
A very common pattern:
nums := []int{3, 1, 2}
sort.Slice(nums, func(i, j int) bool {
return nums[i] < nums[j]
})The closure captures nums and defines the ordering.
Functions as parameters
To accept a function as a parameter, write out its type:
func apply(nums []int, f func(int) int) []int {
out := make([]int, len(nums))
for i, n := range nums {
out[i] = f(n)
}
return out
}
double := func(x int) int { return x * 2 }
apply([]int{1, 2, 3}, double) // [2 4 6]If the signature becomes hard to read, define a type alias:
type Transform func(int) int
func apply(nums []int, f Transform) []int { ... }Try it
Define counter() that returns a closure: each call to the closure increments and returns an internal counter.
Show hint
`n` must be declared BEFORE the return; the closure captures it.
Solution available after 3 attempts
Assign to f a function literal that prints 'ciao', then call it.
Show hint
Syntax: `f := func() { ... }` then `f()`.
Solution available after 3 attempts
What does this print? (watch out for closure scope)
mk := func() func() int {
x := 0
return func() int { x++; return x }
}
a := mk()
b := mk()
fmt.Println(a(), a(), b())Recap
- Functions are first-class: assignable, passable, returnable.
- Function literal:
func(params) ret { ... }, anonymous. - Closure: captures the variables of the lexical scope, keeps them alive.
- Explicit function type in parameters:
func(int) int. - Since Go 1.22, every for iteration has its own copy of the loop variable.