Как системно предотвратить ошибку перезаписи указателей в Go на уровне команды или проекта
Что проверяют
Понимаете ли вы, откуда в Go берутся ситуации, когда два участка кода неожиданно пишут в одну и ту же память, и умеете ли закрывать их процессом, а не надеждой на внимательность. Следом обычно просят показать конкретный пример и назвать инструмент, который его ловит.
Как отвечать
Сначала назовите источники проблемы:
- общий массив у слайсов:
appendв срез, полученный черезs[a:b], может переписать элементы исходного. Лечится копией черезslices.Cloneили трёхиндексным срезомs[a:b:b]; - утечка внутреннего состояния: метод возвращает указатель, слайс или мапу из поля структуры, и вызывающий правит их напрямую. Отдавайте копию через
maps.Cloneилиslices.Clone; - переменная цикла: до Go 1.22
&vвнутриfor rangeуказывал на одну переменную на весь цикл. С 1.22 переменная своя на каждой итерации, но только если вgo.modуказана версияgo 1.22или выше; - гонки: общий указатель в нескольких горутинах без синхронизации.
Дальше меры на уровне команды:
- линтеры в CI:
go vetиgolangci-lintсstaticcheck,gosec,revive. Сборка падает при замечаниях; - тесты с детектором гонок:
go test -race ./...обязательным шагом пайплайна; - соглашения: единый выбор между значением и указателем для ресиверов одного типа, неэкспортируемые поля с конструктором, запрет возвращать внутренние коллекции;
- чек-лист ревью: отдельный пункт про алиасинг и владение данными, принятые правила записаны в ADR.
Где спотыкаются
Предлагают объявить структуру константой. В Go const бывает только у чисел, строк и булевых значений, неизменяемых структур в языке нет.
Называют golint. Он давно объявлен устаревшим и заморожен, его место заняли revive и проверки внутри golangci-lint.
Считают, что -race находит все гонки. Детектор видит только пути, которые реально выполнились в тестах, поэтому без хорошего покрытия он молчит.