Что такое замыкание (closure) в Go и как оно работает?

Ответ

Замыкание (closure) — это функция, которая «запоминает» и имеет доступ к переменным из своей лексической области видимости, даже когда она выполняется вне этой области. В Go анонимные функции часто выступают в роли замыканий.

Ключевые особенности:

  1. Захват переменных: Функция захватывает переменные из внешней области, к которым она обращается.
  2. Время жизни: Захваченные переменные продолжают существовать до тех пор, пока на них ссылается замыкание, даже если внешняя функция уже завершила свою работу.
  3. Изменение переменных: Замыкание может изменять значение захваченных переменных.

Простой пример:

func greeter() func(string) {
    prefix := "Hello, "
    // Эта анонимная функция является замыканием
    return func(name string) {
        fmt.Println(prefix + name) // Использует переменную 'prefix' из внешней функции
    }
}

func main() {
    g := greeter()
    g("Alice") // Выведет: Hello, Alice
    g("Bob")   // Выведет: Hello, Bob
}

Частая ошибка (захват переменной цикла):
Замыкание захватывает ссылку на переменную, а не ее значение в момент создания. Это важно помнить при работе с циклами и горутинами.

// Неправильно: все горутины выведут "10", т.к. i к моменту их запуска уже будет равно 10
for i := 0; i < 10; i++ {
    go func() {
        fmt.Println(i)
    }()
}

// Правильно: передаем значение i в замыкание как аргумент
for i := 0; i < 10; i++ {
    go func(val int) {
        fmt.Println(val)
    }(i)
}