func test(v *Visitor) {
v.work() // error
}
v.work()
должен быть вызовом метода.Но v
имеет тип *Visitor
, указатель на интерфейс. Указатель на интерфейс имеет 0 методов, он ничего не реализует (кроме пустого интерфейса interface{}
).
При использовании не указателя значение v
(или, скорее,его тип) имеет метод work()
, так что вы можете вызвать его так:
func test(v Visitor) {
v.work() // ok
}
Здесь v.work()
работает, потому что v
имеет тип Visitor
, который является интерфейсом, и содержитметод work()
.
Что может сбивать с толку, так это то, что если вы добавляете метод к конкретному типу (не указателю, не интерфейсу), соответствующий тип указателя также будет иметь этот метод, и вы можете вызватьтот.Это в Spec: Наборы методов:
Тип может иметь набор методов , связанный с ним.Набор методов типа интерфейса является его интерфейсом.Набор методов любого другого типа T
состоит из всех методов , объявленных с типом получателя T
. Набор методов соответствующего типа указателя *T
- это набор всех методов, объявленных с получателем *T
или T
(то есть он также содержит набор методов T
). Дополнительные правила применяются к структурам, содержащим встроенные поля, как описано в разделе struct types .Любой другой тип имеет пустой набор методов.В наборе методов каждый метод должен иметь уникальное не- пустое имя метода .
Разница в том, что вы пыталисьто же самое с типом интерфейса, который не будет работать.Работает с конкретными (неинтерфейсными) типами.Урок состоит в том, чтобы никогда не использовать указатель на интерфейс, если вы не можете объяснить, почему он нужен (он нужен редко).