Неожиданное поведение указателей, получаемых в range, при их добавлении в срез

При семантике циклов Go 1.22+ вывод — 10 20. При старой семантике общей переменной v оба указателя после цикла дают 40.

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

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

При семантике циклов Go 1.22+ вывод — 10 20. При старой семантике общей переменной v оба указателя после цикла дают 40.

Подробный разбор

Ответ с пояснениями

Условие

Определите, какие числа будут напечатаны программой, и поясните причину странного результата, возникающего при итерации по массиву с помощью цикла range и сохранении полученных указателей в срез.

package main

import "fmt"

func main() {
  first := []int{10, 20, 30, 40}
  second := make([]*int, len(first))
  for i, v := range first {
    second[i] = &v
  }
  fmt.Println(*second[0], *second[1])
}

Ответ

При семантике циклов Go 1.22+ вывод — 10 20. При старой семантике общей переменной v оба указателя после цикла дают 40.

В новом режиме объявленная через := переменная v создаётся заново на каждой итерации. При старом режиме одна переменная переиспользуется, поэтому все сохранённые адреса совпадают.

Версия семантики зависит в том числе от версии Go, заданной модулем, а не только установленного компилятора. Если нужны указатели именно на элементы first, пишите second[i] = &first[i]: это не адрес копии v и не зависит от изменения семантики циклов. Изменения через такие указатели будут затрагивать исходный срез.

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

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

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

Подробнее