Почему добавляют методы с ресиверами по указателю в структуру с ресиверами по значению
Что проверяют
Знаете ли вы про множество методов типа. Вопрос выглядит придиркой к стилю, но за ним стоит точное правило Go: наборы методов у T и у *T разные, и смешение ресиверов меняет то, какие интерфейсы тип реализует.
Как отвечать
Начните с механики. Метод с ресивером по значению входит и в множество методов T, и в множество *T. Метод с ресивером по указателю входит только в множество *T. Значит, достаточно добавить к типу один метод с ресивером-указателем, и сам T перестаёт удовлетворять интерфейсу, куда этот метод входит:
type Counter struct{ n int }
func (c Counter) Value() int { return c.n }
func (c *Counter) Inc() { c.n++ }
var _ interface{ Inc() } = &Counter{} // ок
// var _ interface{ Inc() } = Counter{} // не компилируетсяДальше объясните, зачем так делают вопреки рекомендации держать ресиверы одинаковыми. Причины всегда практические: метод меняет состояние; структура тяжёлая и копировать её на каждый вызов жалко; внутри лежит sync.Mutex или другое поле, которое нельзя копировать; тип обязан реализовать json.Unmarshaler, sql.Scanner или encoding.TextUnmarshaler, а у них ресивер по определению указатель.
Вывод, который хотят услышать: как только у типа появился хоть один метод с указателем, дальше по коду его передают как *T, а оставшиеся методы по значению живут просто ради читаемости.
Где спотыкаются
Говорят, что вызов через значение сам возьмёт адрес и разницы нет. Go действительно подставляет взятие адреса для адресуемой переменной, но значение в интерфейсе, элемент карты и результат функции неадресуемы, и там такой вызов не скомпилируется.
Второе: копируют значение типа, внутри которого мьютекс. go vet ловит очевидные случаи, но при укладке в интерфейс или присваивании в поле проверка срабатывает не всегда, и две копии начинают защищать разные блокировки.
Третье: меняют поле в методе с ресивером по значению и удивляются, что снаружи ничего не изменилось. Внутри работает копия. Коварнее случай, когда поле это слайс или карта: копия новая, а данные общие, и часть изменений всё-таки видна.