Ответ
Захват переменной (variable capture) — это механизм, при котором анонимная функция (замыкание) получает доступ к переменным из внешней, окружающей её области видимости. Замыкание «захватывает» не значение, а саму переменную, поэтому изменения переменной после создания замыкания будут видны внутри него.
func main() {
a := 1
// Замыкание захватывает переменную 'a'
f := func() {
fmt.Println(a)
}
a = 2 // Изменяем 'a' после создания, но до вызова функции
f() // Выведет 2, так как функция читает актуальное значение 'a'
}
Частая ошибка: захват переменной в цикле
Это классическая проблема при использовании горутин в циклах. Все горутины захватывают одну и ту же переменную, и к моменту их выполнения цикл, скорее всего, уже завершится, и переменная будет иметь последнее значение.
Неправильно:
for i := 0; i < 3; i++ {
// Все горутины захватывают одну и ту же переменную 'i'.
// Скорее всего, все они выведут 3.
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second) // Ждем выполнения горутин
// Возможный вывод: 3, 3, 3
Правильно (передача значения как аргумента):
Чтобы исправить это, нужно передать значение итерационной переменной в анонимную функцию как аргумент. В этом случае для каждой горутины создается своя копия значения.
for i := 0; i < 3; i++ {
// Передаем 'i' как аргумент. 'val' - это локальная копия для каждой горутины.
go func(val int) {
fmt.Println(val)
}(i)
}
time.Sleep(time.Second)
// Вывод (в произвольном порядке): 0, 1, 2 Ответ 18+ 🔞
Да ты посмотри, какая история интересная вырисовывается, блядь! Захват переменной, сука, в замыканиях — это ж как в жизни бывает: ты думаешь, что у тебя всё схвачено, а оно, блядь, уже утекло и поменялось!
Вот смотри, простой пример, чтоб мозги не ебать:
func main() {
a := 1
// Функция-то наша, хитрая жопа, она не значение 'a' запоминает, а саму переменную, как есть, за шкирку хватает!
f := func() {
fmt.Println(a)
}
a = 2 // А мы тут взяли и поменяли 'a'! Уже после того, как функцию создали!
f() // И что она выведет, как думаешь? Правильно, 2, ёпта! Потому что она смотрит на живую переменную, а не на её старый слепок!
}
А вот тут, блядь, классика жанра, где все обламываются!
Особенно когда в цикле горутины запускают, овердохуища народу на этом попались. Все горутины, сука, как мартышлюшки, хватают одну и ту же переменную из цикла, а потом удивляются — а почему у всех одно и то же значение, пиздец!
Так делать — себя не уважать, блядь:
for i := 0; i < 3; i++ {
// Все три горутины, блядь, смотрят на одну и ту же 'i'! К моменту, когда они побегут, цикл уже кончится, и 'i' будет равна 3!
go func() {
fmt.Println(i)
}()
}
time.Sleep(time.Second) // Ждём, пока они там все пошевелятся
// А на выходе получаем: 3, 3, 3. Во все дыры давалка, да? Охуенный результат!
А вот так — правильно, по-белому, блядь:
Надо каждой горутине свою копию значения в лапки сунуть, как аргумент передать. Тогда у каждой будет своё, родное, и драться не будут.
for i := 0; i < 3; i++ {
// Передаём 'i' как аргумент! Теперь у каждой функции своя локальная копия 'val', и им похуй, что там с внешней 'i' происходит.
go func(val int) {
fmt.Println(val)
}(i)
}
time.Sleep(time.Second)
// Вывод будет, конечно, вразнобой (горутины же!), но цифры будут правильные: 0, 1, 2. Красота, ёпта!
Вот и вся магия, блядь. Запомни: когда создаёшь замыкание в цикле, особенно с горутинами — подозрение ебать чувствуй! Сразу думай, что ты там захватываешь, а то получишь пиздопроебибну вместо ожидаемого результата.