Особенности использования указателей внутри цикла range

При семантике Go 1.22+ выводятся 10 20 30 40. При старой семантике range все сохранённые указатели указывают на общую v, и выводится 40 четыре раза.

Короткий ответ

Что ответить на собеседовании

При семантике 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 и адрес элемента — разные вещи.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее