История вопроса:
Конструкция for-range появилась в Go как способ итерации по коллекциям (срезам, массивам, map, строкам). Разработчики Go внедрили оптимизацию: на каждой итерации цикла происходит копирование значения, а не прямое использование по ссылке, что может приводить к неочевидным ошибкам, особенно с переменными цикла.
Проблема:
Многие ошибаются при попытке взятия адреса переменной внутри range (например, &v), считая, что получают адрес элемента коллекции, а на самом деле получают адрес локальной переменной.
Решение:
В цикле for-range на каждой итерации создаются новые копии переменных итератора (key, value). Для простых типов это безболезненно, но для структур приводит к неожиданностям при сохранении указателя на элемент — он всегда будет указывать на одну и ту же переменную, а не на разные элементы среза.
Пример кода:
people := []Person{{Name: "Ivan"}, {Name: "Oleg"}} ptrs := make([]*Person, 0) for _, p := range people { ptrs = append(ptrs, &p) // все ptrs будут ссылаться на один и тот же p }
Ключевые особенности:
Что произойдет при сохранении ссылок на переменную value внутри range?
Все ссылки будут указывать на одну и ту же память, так как value — временная переменная.
for _, v := range someSlice { ptrs = append(ptrs, &v) } // Все ptrs содержат ссылку на одну и ту же переменную!
Можно ли изменить элемент коллекции по ссылке через value в range?
Нет, изменение value не затрагивает исходный элемент коллекции. Для изменения необходимо обратиться по индексу.
for _, v := range arr { v.Field = 10 // arr не изменится } for i := range arr { arr[i].Field = 10 // правильно }
Гарантирует ли for-range по map последовательный порядок обхода?
Нет, порядок итерации по map в Go не определён и может быть разным в каждом запуске приложения.
Разработчик пытается сериализовать список ссылок на элементы структуры через range и сохраняет &value в отдельный срез. Получается срез одинаковых адресов.
Плюсы:
Минусы:
Итерируют по индексу и сохраняют указатель на нужный элемент массива:
for i := range arr { ptrs = append(ptrs, &arr[i]) }
Плюсы:
Минусы: