ProgrammingFullstack Developer

How are anonymous functions implemented and used in Go, and what are the features of their application as function parameters?

Pass interviews with Hintsage AI assistant

Answer.

Background

In Go, anonymous functions (function literals, closures) were introduced to support functional style, callbacks, and concise encapsulation of algorithms. They are often used for processing collections, asynchronous tasks, and passing as parameters.

Problem

Without anonymous functions, the code becomes verbose: each processing task has to be extracted into a separate named function. However, they raise questions: how does variable "capture" work, where is the memory stored, what are the nuances when declaring and passing them as arguments? Is the variable capture safe if they are modified externally? A common error is incorrect variable capture in a loop.

Solution

Anonymous functions are declared as literals and can be assigned to variables or used immediately. If an anonymous function refers to variables from an outer scope, they are "captured" and stored for the life of the closure. As a function parameter, the anonymous function is typically passed with a func type compatible with the signature. Most problems arise with variable capture in loops — if you need to wrap logic in a closure, make sure to create a new variable inside the loop.

Example code:

func operate(nums []int, op func(int) int) []int { res := make([]int, len(nums)) for i, n := range nums { res[i] = op(n) } return res } func main() { arr := []int{1, 2, 3} out := operate(arr, func(x int) int {return x * x}) fmt.Println(out) // [1 4 9] }

Key features:

  • Any func literal in Go can capture variables from an outer scope
  • A closure "lives" as long as there is a reference to it or it is used, even after exiting the original scope
  • Passing an anonymous function as a parameter is straightforward with func type

Tricky questions.

What will happen when capturing a variable with a mutable value in a loop through an anonymous function?

All closures will capture the same variable, and when the function is called after exiting the loop, you will get the same value.

Example code:

func main() { a := []func(){} for i := 0; i < 3; i++ { a = append(a, func() { fmt.Println(i) }) } for _, f := range a { f() } // 3 3 3 }

To avoid this, create a new variable in the loop body:

for i := 0; i < 3; i++ { j := i a = append(a, func() { fmt.Println(j) }) // 0 1 2 }

Can anonymous functions be used as values of type interface{}?

Functions are only compatible with interface{}, not with other interfaces, and they cannot be compared to each other (except nil). If you pass a closure as interface{}, it can only be invoked by casting it to the func signature.

Can anonymous functions be recursive?

Yes, but only if you declare a name variable for the closure first, and then assign the function to it.

Example code:

var fib func(n int) int fib = func(n int) int { if n < 2 { return n } return fib(n-1) + fib(n-2) } fmt.Println(fib(10)) // 55

Common mistakes and anti-patterns

  • Capturing a loop variable without an additional local variable
  • Passing a closure with a large number of heap-captured objects without explicit need
  • Using anonymous functions outside of context when readability and code reusability are required

Real-life example

Negative case

In a loop over a list of callbacks, the developer binds a handler as a closure with a captured iterator variable. All callbacks work with an incorrect value, leading to bugs.

Pros:

  • Minimal boilerplate code

Cons:

  • Widespread error with the loop value, hard-to-catch bug

Positive case

Inside the loop, a new variable is created for each closure, ensuring correct value capture and expected behavior.

Pros:

  • Following recommendations simply helps to avoid bugs
  • Code is concise and safe

Cons:

  • Requires knowledge of closure and scope nuances