sirve - Método de golang con receptor de puntero.
primeros pasos en go (2)
Tengo este código de ejemplo
package main
import (
"fmt"
)
type IFace interface {
SetSomeField(newValue string)
GetSomeField() string
}
type Implementation struct {
someField string
}
func (i Implementation) GetSomeField() string {
return i.someField
}
func (i Implementation) SetSomeField(newValue string) {
i.someField = newValue
}
func Create() IFace {
obj := Implementation{someField: "Hello"}
return obj // <= Offending line
}
func main() {
a := Create()
a.SetSomeField("World")
fmt.Println(a.GetSomeField())
}
SetSomeField
no funciona como se esperaba porque su receptor no es de tipo puntero.
Si cambio el método a un receptor de puntero, lo que esperaría que funcione, se ve así:
func (i *Implementation) SetSomeField(newValue string) { ...
Compilar esto lleva al siguiente error:
prog.go:26: cannot use obj (type Implementation) as type IFace in return argument:
Implementation does not implement IFace (GetSomeField method has pointer receiver)
¿Cómo puedo hacer que la struct
implemente la interfaz y el método SetSomeField
cambie el valor de la instancia real sin crear una copia?
Aquí hay un fragmento hackeable: https://play.golang.org/p/ghW0mk0IuU
Ya he visto esta pregunta en go (golang), ¿cómo se puede convertir un puntero de interfaz en un puntero de estructura? , pero no puedo ver como se relaciona con este ejemplo.
La respuesta simple es que no podrá hacer que la estructura implemente su interfaz mientras que SetSomeField
funcione de la manera que desee.
Sin embargo, un puntero a la estructura implementará la interfaz, por lo que cambiar su método Create
para que return &obj
debería hacer que las cosas funcionen.
El problema subyacente es que su método SetSomeField
modificado SetSomeField
no está en el conjunto de métodos de Implementation
. Si bien la *Implementation
del tipo *Implementation
heredará los métodos del receptor sin puntero, lo contrario no es cierto.
El motivo de esto está relacionado con la forma en que se especifican las variables de la interfaz: la única forma de acceder al valor dinámico almacenado en una variable de la interfaz es copiarlo. Como ejemplo, imagina lo siguiente:
var impl Implementation
var iface IFace = &impl
En este caso, una llamada a iface.SetSomeField
funciona porque puede copiar el puntero para usar como el receptor en la llamada de método. Si almacenamos directamente una estructura en la variable de interfaz, deberíamos crear un puntero a esa estructura para completar la llamada al método. Una vez que se hace dicho puntero, es posible acceder (y potencialmente modificar) el valor dinámico de la variable de la interfaz sin copiarlo.
Su puntero a la estructura debe implementar la interfaz. De esa forma podrás modificar sus campos.
Mira cómo modifiqué tu código para que funcione como esperas:
package main
import (
"fmt"
)
type IFace interface {
SetSomeField(newValue string)
GetSomeField() string
}
type Implementation struct {
someField string
}
func (i *Implementation) GetSomeField() string {
return i.someField
}
func (i *Implementation) SetSomeField(newValue string) {
i.someField = newValue
}
func Create() *Implementation {
return &Implementation{someField: "Hello"}
}
func main() {
var a IFace
a = Create()
a.SetSomeField("World")
fmt.Println(a.GetSomeField())
}