В Go анонимные функции (func literals) способны создавать замыкания, то есть получать доступ к переменным из окружающей области видимости даже после её завершения. Такие замыкания выделяют память в heap, если это нужно для корректной работы (detected via escape analysis).
Пример:
func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } a := adder() printf("%d ", a(5)) // 5 printf("%d ", a(10)) // 15
Особенности:
Что выведет этот код?
func main() { fs := []func(){} for i := 0; i < 3; i++ { fs = append(fs, func() { fmt.Println(i) }) } for _, f := range fs { f() } }
Многие ответят, что выведет 0, 1, 2, однако результат будет:
3
3
3
Все замыкания ссылаются на одну и ту же переменную i; после завершения цикла её значение — 3.
Правильно: захватывать копию переменной в теле цикла:
for i := 0; i < 3; i++ { v := i // новая переменная fs = append(fs, func() { fmt.Println(v) }) }
История
В проекте динамического роутинга использовали цикл для создания множества handler-ов через closure, каждый должен был захватить свой путь. В результате все handler-ы печатали последний путь — не создали отдельную переменную в каждом замыкании. Ошибка обнаружилась только при интеграции с HTTP API.
История
При тестировании параллельного доступа через горутины внутри цикла closure захватывало ссылку на индекс, а не копию. Это создавало "случайные" эффекты: данные записывались не в свой слот массива, а в последний.
История
В функции сбора статистики closure изменяло общую переменную из внешней области, хотя автор ожидал независимый счетчик для каждой задачи. Проблему заметили по неадекватно реконструируемой сумме, которая всегда была общей, не частной, несмотря на локальную логику.