Что такое указатель в Go и когда его использовать
Что проверяют
Понимаете ли вы, что в Go всё передаётся по значению, и указатель это способ явно сказать «работай с оригиналом, а не с копией». Следом почти всегда спрашивают про ресиверы методов: когда func (s *Server), а когда func (s Server), и где окажется переменная, на стеке или в куче.
Как отвечать
Указатель *T хранит адрес значения типа T. Адрес берут оператором &, разыменовывают через *, нулевое значение указателя nil. Арифметики указателей в Go нет, она доступна только через пакет unsafe. Создать указатель можно через &T{...} или new(T).
Когда указатель нужен:
- изменить значение в функции: без указателя функция получит копию, и изменения потеряются;
- ресивер метода: если хотя бы один метод меняет состояние, делайте указательными ресиверы у всего типа. Go Code Review Comments советует не смешивать их без причины;
- тип нельзя копировать: структура с
sync.Mutexилиsync.WaitGroupвнутри передаётся только по указателю, копирование ловитgo vet; - большая структура: копирование сотен байт на каждом вызове заметно в горячем пути;
- нужно отличить «не задано» от нуля:
*intв DTO или в необязательном поле конфигурации.
Когда не нужен: слайсы, мапы и каналы уже содержат ссылку на данные внутри, и функция без всякого указателя увидит изменения элементов. Мелкие структуры дешевле копировать.
Добавьте про escape analysis: компилятор сам решает, где разместить переменную. Вернуть &x из функции в Go безопасно, просто x уедет в кучу. Посмотреть решения компилятора можно через go build -gcflags=-m.
Где спотыкаются
Считают, что указатель всегда быстрее. Значение, ушедшее в кучу, это аллокация и работа для сборщика мусора, а мелкая структура на стеке обходится дешевле.
Забывают про наборы методов: если методы объявлены на *T, то значение T интерфейс не реализует, и компилятор скажет об этом при присваивании.
Не проверяют nil там, где указатель может прийти пустым, и получают панику invalid memory address or nil pointer dereference. Рядом гонки: несколько горутин с одним указателем без синхронизации дают data race, который ловится go test -race.