受信器とインターフェイス

値の受信器を持つメソッドは、値とポインタの両方を使用して呼び出すことができます。

ポインタの受信器を持つメソッドは、ポインタまたはアドレス可能な値を使用してのみ呼び出すことができます。

例:

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が値であり、S2のfメソッドに値の受信器がないため、以下のコードはコンパイルされません
// i = s2Val

Effective Go には、ポインタと値に関する優れた説明が含まれています。

追加の注意:

  • 型は値の受信器を持つメソッドセットとポインタの受信器を持つメソッドセットを持つことができます。
    • 値の受信器を持つメソッドセットはポインタの受信器を持つメソッドセットの部分集合ですが、その逆ではありません。
  • ルール
    • 値オブジェクトは値の受信器を持つメソッドセットのみを使用できます。
    • ポインタオブジェクトは値の受信器を持つメソッドセット+ポインタの受信器を持つメソッドセットを使用できます。
  • インターフェイスの一致(または実装)
    • 型はインターフェイスのすべてのメソッドを実装している場合、その型がインターフェイスに一致します。
    • 具体的には、型の値メソッドセットがインターフェースに一致するか、ポインタメソッドセットがインターフェースに一致するかです。

一致には2つの特定のタイプがあります:

  • 値メソッドセットがインターフェースに一致する
    • 値またはポインタオブジェクトをインターフェイス変数に割り当てることは問題ありません。どちらも値メソッドセットを含んでいるためです。
  • ポインタメソッドセットがインターフェースに一致する
    • ポインタオブジェクトのみがインターフェイス変数に割り当てることができます。ポインタメソッドセットのみがインターフェイスに一致するためです。
    • 値オブジェクトがインターフェース変数に割り当てられた場合、コンパイル中にインターフェイスの生存確認機構がトリガーされます。

なぜ i = s2Val がエラーを発生させるのかについては、値メソッドセットとインターフェイスが一致しないためです。