Comments 11
Кто не ловил захват переменной в цикле , тот не писал на go
База + update. Спасибо!
Копирование элементов. Напомню, что каждый раз, когда вы выполняете обход массивов и слайсов, Go создает копию каждого элемента
Это вполне ожидаемое поведение, я не припомню языков где иначе (даже если значения массива ссылки/указатели, то по сути имеем их копию). А вот про то, что выражение range создаёт копию самого массива/слайса можно было упомянуть. В случае массива это полноценная копия, в случае слайса копия только описывающей слайс структуры. Отсюда всякие нюансы с изменением во время итерации, их полезно знать.
База + update. Благодарю
В этом примере удаление элементов из
map
во время итерации небезопасно и может привести к ошибкам во время выполнения или неожиданному поведению, поскольку самаmap
изменяется во время итерации.
Это откуда такая информация? Если почитать практику и спеки, везде написано, что это безопасно, ну или наоборот нигде не написано что так делать нельзя. Конкурирующий доступ запрещен да. Модификация мапы в одном потоке разрешена. Про range по мапе в спеке написано, что удаленные элементы, до которых вы еще не дошли не будут пройдены в цикле (что звучит логично), а добавленные могут а могут и не быть пройдены (т.к. порядок не гарантирован)
Добавлю ссылку на спеку https://go.dev/ref/spec#For_range
3. The iteration order over maps is not specified
and is not guaranteed to be the same from one iteration to the next.
If a map entry that has not yet been reached is removed during iteration,
the corresponding iteration value will not be produced. If a map entry is
created during iteration, that entry may be produced during the iteration or
may be skipped. The choice may vary for each entry created and from one
iteration to the next.
Что делает собственно ключевое слово range? Можно ли его использовать вне цикла? Или это просто такое украшательство чтобы отличить for по индексу от for по диапазону?
range итерирует структуры, которые описаны в статье (слайс/массив, мапа, строка), вне цикла использовать его не получится - он часть синтаксиса. При этом это не "синтаксический сахар" к более длинным конструкциям: заменить его на на что-то другое можно для строк и массивов, но не для мап - т.к. отсутствуют публичные методы для получения итератора мапы.
исходники его тут кстати https://github.com/golang/go/blob/master/src/cmd/compile/internal/walk/range.go
Главная новость заключается в том, что Go 1.22 изменяет правила определения области видимости для переменных в циклах
for
. Раньше переменные цикла имели область видимости для всего цикла. Теперь каждая итерация получает свою собственную область видимости
Но
Копирование элементов. Напомню, что каждый раз, когда вы выполняете обход массивов и слайсов, Go создает копию каждого элемента
т.е. в цикле
for i,val = range arr {}
Переменная val это копия (на стеке) в каждой итерации и эта копия со стека удаляется в конце каждой итерации и в начале следующей создается другая копия. Т.е. область видимости копии и так ограничена одной итерацией. Или нет? В чем тогда нововведение 1.22 в части области видимости копии?
Интересное нововведение. Интересно как они это реализовали. Предполагаю, что создаётся новая переменная под капотом и уже она используется в другой closure. Другими словами - перехват переменной делают за программиста. А без этого все остается как было
Range Loop в Go: подводные камни, как с ними бороться и что нас ждёт в версии 1.22