В Go замыкания (closures) — это функции, которые "замыкают" (захватывают) переменные своей окружающей области видимости. Чаще всего замыкания используются для анонимных функций, созданных внутри других функций.
Наиболее типичная проблема при работе с замыканиями — неожиданное поведение при использовании переменных цикла внутри горутины:
for i := 0; i < 3; i++ { go func() { fmt.Println(i) }() }
Каждая горутина может напечатать одно и то же значение i, потому что переменная i циклит, и замыкание захватывает именно переменную, а не её значение на каждой итерации.
Правильный способ:
for i := 0; i < 3; i++ { go func(val int) { fmt.Println(val) }(i) }
Такое поведение связано с тем, что замыкание держит ссылку на переменную (её адрес), а не её срезанное (by value) значение.
Какое значение напечатают несколько запущенных горутин внутри цикла, если они захватывают переменную цикла?
Ответ: Все горутины могут напечатать одно и то же значение (часто последнее), так как они видят текущее значение переменной, а к моменту выполнения горутины цикл уже завершился. Чтобы избежать этого, переменную нужно передавать как параметр в замыкание.
Пример:
for i := 0; i < 5; i++ { go func() { fmt.Println(i) }() } // вероятнее всего получим: 5 5 5 5 5
История
История
История
В курьерском стартапе некорректное использование замыканий при обновлении координат заказа приводило к тому, что массово обновлялись координаты последнего заказа в срезе, а не текущего — из-за гонки при обращении к срезу внутри анонимной функции.