Receiver and Interface

Methods with value receivers can be called using both values and pointers.

Methods with pointer receivers can only be called using pointers or addressable values.

For example,

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"}}

// You can only call Read using a value
sVals[1].Read()

// This will not compile:
// sVals[1].Write("test")

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

// Both Read and Write methods can be called using a pointer
sPtrs[1].Read()
sPtrs[1].Write("test")

Similarly, even if a method has a value receiver, it can satisfy an interface using a pointer receiver.

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

// The following code will not compile because s2Val is a value and the f method of S2 does not have a value receiver
// i = s2Val

Effective Go contains excellent explanations about pointers vs. values.

Additional notes:

  • A type can have a method set with value receivers and a method set with pointer receivers
    • The method set with value receivers is a subset of the method set with pointer receivers, but not vice versa
  • Rules
    • Value objects can only use the method set with value receivers
    • Pointer objects can use the method set with value receivers + the method set with pointer receivers
  • Interface matching (or implementation)
    • Type matches the interface if it implements all the interface’s methods
    • Specifically, it’s either the type’s value method set matching the interface or the pointer method set matching the interface

There are two specific types of matching:

  • Value method set matching the interface
    • It’s okay to assign a value or pointer object to an interface variable since both contain the value method set
  • Pointer method set matching the interface
    • Only a pointer object can be assigned to an interface variable as only the pointer method set matches the interface
    • If a value object is assigned to an interface variable, it will trigger an interface liveness check mechanism during compilation

As for why i = s2Val triggers an error, it’s because the value method set and the interface do not match.