Получатель и интерфейс

Методы с получателями значений могут быть вызваны как с использованием значений, так и с указателей.

Методы с получателями указателей могут быть вызваны только с использованием указателей или адресуемых значений.

Например,

type S struct {
  data string
}

func (s S) Read() string {
  return s.data
}

func (s *S) Write(str string) {
  s.data = str
}

sVals := map[int]S{1: {"A"}}

// Вы можете вызвать только Read, используя значение
sVals[1].Read()

// Это не скомпилируется:
// sVals[1].Write("test")

sPtrs := map[int]*S{1: {"A"}}

// Оба метода Read и Write могут быть вызваны, используя указатель
sPtrs[1].Read()
sPtrs[1].Write("test")

Точно так же, даже если у метода есть получатель значения, он может удовлетворить интерфейс, используя получатель указателя.

type F interface {
  f()
}

type S1 struct{}

func (s S1) f() {}

type S2 struct{}

func (s *S2) f() {}

s1Val := S1{}
s1Ptr := &S1{}
s2Val := S2{}
s2Ptr := &S2{}

var i F
i = s1Val
i = s1Ptr
i = s2Ptr

// Следующий код не скомпилируется, потому что s2Val является значением и метод f типа S2 не имеет получателя значения
// i = s2Val

В Effective Go содержатся отличные объяснения о указателях против значений.

Дополнительные заметки:

  • У типа может быть набор методов с получателями значений и набор методов с получателями указателей
    • Набор методов с получателями значений является подмножеством набора методов с получателями указателей, но не наоборот
  • Правила
    • Объекты значений могут использовать только набор методов с получателями значений
    • Объекты указателей могут использовать набор методов с получателями значений + набор методов с получателями указателей
  • Соответствие интерфейсу (или реализация)
    • Тип соответствует интерфейсу, если он реализует все методы интерфейса
    • Конкретно, набор методов значения типа соответствует интерфейсу или набор методов указателя соответствует интерфейсу

Существует два конкретных типа соответствия:

  • Соответствие набора методов значения интерфейсу
    • В порядке можно присвоить переменной интерфейса как объект значения, так и указатель, так как оба содержат набор методов значения
  • Соответствие набора методов указателя интерфейсу
    • Только объект указателя может быть присвоен переменной интерфейса, поскольку только набор методов указателя соответствует интерфейсу
    • Если объект значения присваивается переменной интерфейса, это повлечет за собой проверку активности интерфейса во время компиляции

По поводу того, почему i = s2Val вызывает ошибку, это потому, что набор методов значения и интерфейс не соответствуют.