ПрограммированиеBackend разработчик

Опишите особенности работы с указателями (pointers) в Go: когда их стоит использовать, чем они отличаются от ссылок в других языках, и какие типовые ловушки связаны с их применением?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Go указатель представляет собой переменную, хранящую адрес другой переменной. В отличие от некоторых языков, в Go невозможно выполнять арифметику указателей, что делает их безопаснее. Указатели в Go часто применяют для:

  • Передачи больших структур в функции (во избежание копирования).
  • Изменения объекта из внешней функции.
  • Для реализации сложных структур данных (например, списков, деревьев).

В Go отсутствует явная поддержка ссылочных типов, как, например, в C++, однако slices и maps уже являются ссылочными по своей природе, и их часто передают по значению.

Пример передачи указателя и значения:

package main import "fmt" type User struct { Name string } func changeNameByValue(u User) { u.Name = "Вася" } func changeNameByPointer(u *User) { u.Name = "Петя" } func main() { user := User{Name: "Иван"} changeNameByValue(user) fmt.Println(user.Name) // Иван changeNameByPointer(&user) fmt.Println(user.Name) // Петя }

Вопрос с подвохом.

Могут ли указатели указывать на константы или литералы в Go?

Правильный ответ: Нет, в Go нельзя брать адрес у константы или у литералов напрямую. Адрес можно взять только у переменной:

x := 5 p := &x // OK p2 := &10 // Ошибка компиляции

Это часто путают с языками, где подобное допустимо.

Примеры реальных ошибок из-за незнания тонкостей темы.


История

На одном проекте разработчик попытался передавать ссылку на элемент слайса, считая, что сможет безопасно модифицировать слайс через указатель. Но ошибся: слайс сам по себе содержит указатель, но после append() адрес подложенного массива меняется, и изменения не всегда отражались на исходных данных. Это привело к трудноуловимым багам (данные "терялись").


История

В проекте был создан список указателей на локальные переменные в цикле, и после выхода из цикла все указатели указывали на одну и ту же переменную (итерируемую переменную цикла). Ошибку заметили только после падения в продакшене.


История

Разработчик ошибочно решил, что присваивание указателя освобождает предыдущий объект из памяти, и явно присваивал указателю nil, ожидая GC сразу. На практике в Go сборщик мусора сам решает, когда чистить объекты, игнорируя преждевременное обнуление ссылок. Это привело к утечкам там, где рассчитывали на своевременное высвобождение памяти.