Можно ли в одном типе миксовать методы с ресиверами по значению и по указателю и хорошо ли это
Что проверяют
Понимаете ли вы наборы методов (method sets) и как тип ресивера влияет на реализацию интерфейсов. Следом обычно спрашивают, почему значение типа вдруг «не реализует» интерфейс, хотя все методы на месте.
Как отвечать
Компилятор смешивание разрешает, но Go Code Review Comments прямо советует не делать так: выберите один вид ресивера для всех методов типа. Аргументы:
- наборы методов: у
Tв набор входят только методы с ресивером по значению, у*Tвсе. ЕслиWriteобъявлен на*Buffer, тоvar w io.Writer = Buffer{}не скомпилируется, нужен&Buffer{}; - неявное взятие адреса:
v.Inc()для адресуемой переменной компилятор превращает в(&v).Inc(), поэтому смешение в обычном коде незаметно. Но элемент map неадресуем, иm[k].Inc()не соберётся; - копирование: метод по значению копирует всю структуру. Если внутри
sync.Mutex, копия блокировки ничего не защищает, это ловитgo vet(проверкаcopylocks); - гонки: метод по значению читает все поля при копировании, пока другая горутина пишет через метод по указателю. Race detector такое ловит;
- nil: вызов метода по значению на nil-указателе паникует, а метод по указателю может nil обработать.
Правило выбора: если хоть один метод меняет состояние, структура крупная или содержит мьютекс, делайте указатель везде. По значению везде подходит маленьким неизменяемым типам. Исключение есть в стандартной библиотеке: у time.Time методы по значению, а UnmarshalJSON по указателю, потому что обязан менять получатель. Для контроля в golangci-lint есть линтер recvcheck.
Где спотыкаются
Отвечают «можно, это вопрос стиля» и не упоминают method sets. Проблема не в эстетике: тип начинает реализовывать интерфейс только через указатель.
Считают, что указатель всегда быстрее. Маленькую структуру скопировать дёшево, а указатель добавляет разыменование и может увести значение в кучу.
Думают, что метод по значению защищает данные от изменения. Копия поверхностная: слайсы, map и указатели внутри по-прежнему смотрят в общие данные.