В Go анонимные функции (function literals, closures) появились для поддержки функционального стиля, обратных вызовов и лаконичной инкапсуляции алгоритмов. Они часто используются для обработки коллекций, асинхронных задач и передачи как параметров.
Без анонимных функций код становится избыточным: каждую обработку приходится выносить в отдельную именованную функцию. Но с ними возникают вопросы: как работает "захват" переменных, где хранится память, какие особенности есть при объявлении и передачи как аргументов? Защищён ли захват переменных, если их изменяют снаружи? Частая ошибка — некорректный захват переменной в цикле.
Анонимные функции объявляются как литералы и могут быть присвоены переменным или сразу использоваться. Если анонимная функция обращается к переменным внешней области, они "захватываются" и сохраняются для жизни closure. Как параметр функции, анонимная функция передаётся обычно с типом func, совместимым с сигнатурой. Большинство проблем возникает при захвате переменных цикла — тут, если нужно обернуть логику в closure, обязательно создавайте новую переменную внутри цикла.
Пример кода:
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] }
Ключевые особенности:
Что будет при захвате переменной с изменяемым значением в цикле через анонимную функцию?
Все closure захватят одну и ту же переменную, и при вызове функции после выхода из цикла вы получите одно и то же значение.
Пример кода:
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 }
Чтобы избежать этого, создавайте новую переменную в теле цикла:
for i := 0; i < 3; i++ { j := i a = append(a, func() { fmt.Println(j) }) // 0 1 2 }
Можно ли использовать анонимные функции как значения типа interface{}?
Функции совместимы только с interface{}, не с другими интерфейсами, их нельзя сравнивать между собой (кроме nil). Если передать closure как interface{}, можно вызвать только через приведение типа к func сигнатуре.
Могут ли анонимные функции быть рекурсивными?
Да, но только если сначала объявить переменную-имя для closure, а потом присвоить функцию ей же.
Пример кода:
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
В цикле по списку callback'ов разработчик привязывает обработчик как closure с захватом переменной-итератора. Все callback'и работают с несоответствующим значением, ведут к багам.
Плюсы:
Минусы:
Внутри цикла создают новую переменную для каждого closure, гарантируя корректный захват значения и ожидаемое поведение.
Плюсы:
Минусы: