I have this example code
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
does not work as expected because its receiver is not of pointer type.
If I change the method to a pointer receiver, what I would expect to work, it looks like this:
func (i *Implementation) SetSomeField(newValue string) { ...
Compiling this leads to the following 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)
How can I have the struct
implement the interface and the method SetSomeField
change the value of the actual instance without creating a copy?
Here's a hackable snippet: https://play.golang.org/p/ghW0mk0IuU
I've already seen this question In go (golang), how can you cast an interface pointer into a struct pointer?, but I cannot see how it is related to this example.
you can mix and match methods with value receivers and methods with pointer receivers, and use them with variables containing values and pointers, without worrying about which is which. Both will work, and the syntax is the same.
You can declare methods with pointer receivers. This means the receiver type has the literal syntax *T for some type T . (Also, T cannot itself be a pointer such as *int .) For example, the Scale method here is defined on *Vertex .
There are two reasons to use a pointer receiver. The first is so that the method can modify the value that its receiver points to. The second is to avoid copying the value on each method call.
Go methods are similar to Go function with one difference, i.e, the method contains a receiver argument in it. With the help of the receiver argument, the method can access the properties of the receiver. Here, the receiver can be of struct type or non-struct type.
Your pointer to the struct should implement the Interface. In that way you can modify its fields.
Look at how I modified your code, to make it working as you expect:
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()) }
The simple answer is that you won't be able to have the struct implement your interface while having SetSomeField
work the way you want.
However, a pointer to the struct will implement the interface, so changing your Create
method to do return &obj
should get things working.
The underlying problem is that your modified SetSomeField
method is no longer in the method set of Implementation
. While the type *Implementation
will inherit the non-pointer receiver methods, the reverse is not true.
The reason for this is related to the way interface variables are specified: the only way to access the dynamic value stored in an interface variable is to copy it. As an example, imagine the following:
var impl Implementation var iface IFace = &impl
In this case, a call to iface.SetSomeField
works because it can copy the pointer to use as the receiver in the method call. If we directly stored a struct in the interface variable, we'd need to create a pointer to that struct to complete the method call. Once such a pointer is made, it is possible to access (and potentially modify) the interface variable's dynamic value without copying it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With