Receptor e Interface

Métodos com receptores de valor podem ser chamados usando tanto valores quanto ponteiros.

Métodos com receptores de ponteiro só podem ser chamados usando ponteiros ou valores endereçáveis.

Por exemplo,

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

// Você só pode chamar Read usando um valor
sVals[1].Read()

// Isso não irá compilar:
// sVals[1].Write("test")

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

// Tanto os métodos Read quanto Write podem ser chamados usando um ponteiro
sPtrs[1].Read()
sPtrs[1].Write("test")

Da mesma forma, mesmo que um método tenha um receptor de valor, ele pode satisfazer uma interface usando um receptor de ponteiro.

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

// O código a seguir não irá compilar porque s2Val é um valor e o método f de S2 não possui um receptor de valor
// i = s2Val

Effective Go contém excelentes explicações sobre ponteiros vs. valores.

Notas adicionais:

  • Um tipo pode ter um conjunto de métodos com receptores de valor e um conjunto de métodos com receptores de ponteiro
    • O conjunto de métodos com receptores de valor é um subconjunto do conjunto de métodos com receptores de ponteiro, mas não o contrário
  • Regras
    • Objetos de valor só podem usar o conjunto de métodos com receptores de valor
    • Objetos de ponteiro podem usar o conjunto de métodos com receptores de valor + o conjunto de métodos com receptores de ponteiro
  • Correspondência de interface (ou implementação)
    • O tipo corresponde à interface se implementar todos os métodos da interface
    • Especificamente, é o conjunto de métodos de valor do tipo correspondendo à interface ou o conjunto de métodos de ponteiro correspondente à interface

Existem dois tipos específicos de correspondência:

  • Conjunto de métodos de valor correspondendo à interface
    • É aceitável atribuir um valor ou objeto de ponteiro a uma variável de interface, uma vez que ambos contêm o conjunto de métodos de valor
  • Conjunto de métodos de ponteiro correspondendo à interface
    • Apenas um objeto de ponteiro pode ser atribuído a uma variável de interface, já que apenas o conjunto de métodos de ponteiro corresponde à interface
    • Se um objeto de valor é atribuído a uma variável de interface, irá acionar um mecanismo de verificação de vivacidade da interface durante a compilação

Quanto ao motivo pelo qual i = s2Val aciona um erro, é porque o conjunto de métodos de valor e a interface não correspondem.