Метод на указателе — и interface не реализован

Классика собеса на Go: дают тип с методом на указателе и просят присвоить значение (не указатель) переменной интерфейсного типа.

type Animal interface { Speak() string }

type Dog struct { Name string }

func (d *Dog) Speak() string { return d.Name + ": woof" }

func main() { var a Animal = Dog{Name: "Rex"} _ = a }

Этот код не компилируется. Ошибка компилятора: Dog does not implement Animal (method Speak has pointer receiver).

Причина в наборе методов (method set). У типа T набор методов — только методы с value receiver. У типа *T — методы и с value, и с pointer receiver. Метод Speak() объявлен на *Dog, значит он входит в method set только *Dog, но не Dog.

Правка простая — передать указатель:

var a Animal = &Dog{Name: "Rex"}

Это компилируется, потому что *Dog реализует интерфейс.

Следом обычно спрашивают: а почему вообще так сделано, почему компилятор не может «просто взять адрес» автоматически? Ответ: Go мог бы разрешить это для адресуемых значений (переменных), но не для временных значений вроде литерала в интерфейсной переменной — у которых нет стабильного адреса в момент присваивания. Правило единое и консистентное: метод с pointer receiver требует именно указатель, без исключений на уровне интерфейсов. Второй частый вопрос: если у структуры есть хотя бы один метод с pointer receiver, стоит ли остальные методы тоже делать на указателе — да, обычно да, чтобы не путать method set и не ловить такие ошибки на других методах того же типа.

Если структура участвует в интерфейсах — выбор receiver не стилистический вопрос, а вопрос совместимости с конкретным интерфейсным типом.

Тренажёр: 1900+ вопросов и задач, мок с таймером, план повторов

senior·base — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки