При семантике Go 1.22+ выводятся 10 20 30 40. При старой семантике range все сохранённые указатели указывают на общую v, и выводится 40 четыре раза.
Особенности использования указателей внутри цикла range
При семантике Go 1.22+ выводятся 10 20 30 40. При старой семантике range все сохранённые указатели указывают на общую v, и выводится 40 четыре раза.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Вопрос: какие числа будут напечатаны, если сохранять ссылки на переменную‑итератор внутри цикла range, и почему полученный вывод может отличаться от ожидаемого?
package main
import (
"fmt"
)
func main() {
var ptrs []*int // срез указателей
for _, v := range []int{10, 20, 30, 40} {
ptrs = append(ptrs, &v) // сохраняем адрес переменной‑итератора
}
for _, p := range ptrs {
fmt.Printf("%d ", *p) // выводим значение, на которое указывает каждый указатель
}
}
Ответ
При семантике Go 1.22+ выводятся 10 20 30 40. При старой семантике range все сохранённые указатели указывают на общую v, и выводится 40 четыре раза.
Новое правило создаёт отдельную объявленную переменную v на каждой итерации. В старом режиме она переиспользуется, а после цикла содержит последнее значение.
Это зависит от языковой версии модуля, не только версии установленного компилятора. Для совместимости со старым поведением можно внутри цикла сделать v := v перед взятием адреса. Если нужны адреса самих элементов исходного среза, используйте индекс и &values[i]: адрес копии v и адрес элемента — разные вещи.