Receptor e interfaz

Los métodos con receptores de valor pueden ser llamados usando tanto valores como punteros.

Los métodos con receptores de puntero solo pueden ser llamados usando punteros o valores direccionables.

Por ejemplo,

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

// Solo puedes llamar a Read usando un valor
sVals[1].Read()

// Esto no compilará:
// sVals[1].Write("test")

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

// Tanto los métodos Read como Write pueden ser llamados usando un puntero
sPtrs[1].Read()
sPtrs[1].Write("test")

Del mismo modo, incluso si un método tiene un receptor de valor, puede satisfacer una interfaz usando un receptor de puntero.

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

// El siguiente código no compilará porque s2Val es un valor y el método f de S2 no tiene un receptor de valor
// i = s2Val

Effective Go contiene excelentes explicaciones sobre punteros vs. valores.

Notas adicionales:

  • Un tipo puede tener un conjunto de métodos con receptores de valor y un conjunto de métodos con receptores de puntero
    • El conjunto de métodos con receptores de valor es un subconjunto del conjunto de métodos con receptores de puntero, pero no al revés
  • Reglas
    • Los objetos de valor solo pueden usar el conjunto de métodos con receptores de valor
    • Los objetos de puntero pueden usar el conjunto de métodos con receptores de valor + el conjunto de métodos con receptores de puntero
  • Coincidencia de interfaz (o implementación)
    • El tipo coincide con la interfaz si implementa todos los métodos de la interfaz
    • Específicamente, ya sea el conjunto de métodos de valor del tipo que coincide con la interfaz o el conjunto de métodos de puntero que coincide con la interfaz

Hay dos tipos específicos de coincidencia:

  • Coincidencia del conjunto de métodos de valor con la interfaz
    • Es correcto asignar un objeto de valor o puntero a una variable de interfaz ya que ambos contienen el conjunto de métodos de valor
  • Coincidencia del conjunto de métodos de puntero con la interfaz
    • Solo un objeto de puntero puede ser asignado a una variable de interfaz, ya que solo el conjunto de métodos de puntero coincide con la interfaz
    • Si se asigna un objeto de valor a una variable de interfaz, esto activará un mecanismo de verificación de vitalidad de la interfaz durante la compilación

En cuanto a por qué i = s2Val desencadena un error, es porque el conjunto de métodos de valor y la interfaz no coinciden.