Что напечатает цикл после переприсваивания slice во время range?
Предскажите вывод и объясните почему.
func main() {
lst := []string{"a", "b", "c", "d"}
for k, v := range lst {
if k == 0 {
lst = []string{"aa", "bb", "cc", "dd"}
}
fmt.Println(v)
}
}
Объясните вывод и что изменилось бы при lst[3] = "z".
Печатает a b c d. range вычисляет операнд один раз в начале и итерируется по копии заголовка slice (указатель, len, cap). Переприсваивание lst совершенно новому slice лишь переназначает переменную — цикл продолжает идти по исходному backing-массиву. Если же изменить элемент исходного backing-массива, например lst[3] = "z", цикл это увидит и напечатает a b c z.
- ✗Думать, что
rangeперечитывает переменную slice на каждой итерации - ✗Считать, что переприсваивание переменной меняет итерируемый backing-массив
- ✗Ожидать panic вместо стабильного обхода исходного массива
- →Почему изменение
lst[3]видно в цикле, а переприсваиваниеlst— нет? - →Какие три поля копируются, когда
rangeделает снимок заголовка slice?
Что выведет
a b c d.
range lst вычисляет операнд один раз в начале цикла и работает с копией заголовка slice — структуры из трёх полей: указателя на backing-массив, len и cap. Сам backing-массив не копируется, но переменная lst и итератор range теперь независимы.
Строка lst = []string{"aa", "bb", "cc", "dd"} создаёт совершенно новый slice с новым backing-массивом и привязывает к нему переменную lst. Итератор range про это ничего не знает — он продолжает идти по исходному массиву ["a","b","c","d"].
Это иллюстрация принципа «в Go всё передаётся по значению»: копируется заголовок slice, а не массив.
Если менять элемент на месте
for k, v := range lst {
if k == 0 {
lst[3] = "z" // меняем исходный backing-массив
}
fmt.Println(v)
}
Теперь правка попадает в тот самый массив, по которому идёт итератор → выведет a b c z.