Skip to content

Instantly share code, notes, and snippets.

@wodor
Last active October 27, 2021 09:41
Show Gist options
  • Select an option

  • Save wodor/5ef65c1cc82119059fbad82ed5bea8b5 to your computer and use it in GitHub Desktop.

Select an option

Save wodor/5ef65c1cc82119059fbad82ed5bea8b5 to your computer and use it in GitHub Desktop.
Demonstrates how pointers work for methods with interfaces as parameters
package main
import "fmt"
func main() {
i := TheImplementation{Value: 0}
// simple, changes the value as expected
toPointer(&i)
fmt.Printf(" V: %d \n", i.Value)
// works fine too but has to use get/setters to access the value
toInterface(&i)
fmt.Printf(" V: %d \n", i.Value)
// uses a method (with pointer receiver) from underlying interface
toInterfaceViaRole(&i)
fmt.Printf(" V: %d \n", i.Value)
// cant do it because pointer receivers on some interface implementation methods
//toInterface(i)
//fmt.Printf(" V: %d \n", i.Value)
// not going to change i, will change the copy of i
toValue(i)
fmt.Printf(" V: %d \n", i.Value)
// passing interface implementation instance by value, possible because doA() has value receiver
// it won't change the i.Value, of course
toRoleInterface(i)
fmt.Printf(" V: %d \n", i.Value)
// but even when passed by reference, Value will not change here
// because value receiver of doA() will create a copy and make change on that copy
// and obviously we can't access the field within the method when it's argument is an interface
toRoleInterface(&i)
// it is hard to spot that this will not work as expected (I passed the reference!)
// one has to look into the specific implementation of doA() for passed struct and see the value receiver
fmt.Printf(" V: %d \n", i.Value)
// conclusions:
// always use pointer receivers in methods meant to change state of received struct (any reason to use value receivers anytime?)
// always pass the interface implementation instance as reference (clone explicitly when needed?)
}
func toPointer(i *TheImplementation) {
i.Value++
fmt.Printf("to pointer: %p %p %p", i, &i, *i)
}
func toValue(i TheImplementation) {
i.Value++
fmt.Printf("to value: %p %p", i, &i)
}
func toInterface(i TheHeader) {
i.SetValue(i.GetValue() + 1)
fmt.Printf("to interface: %p %p", i, &i)
}
func toInterfaceViaRole(i TheHeader) {
i.doB()
fmt.Printf("to InterfaceViaRole: %p %p", i, &i)
}
//func toInterfaceHack(i TheHeader) {
// z := *i
// z.SetValue(z.GetValue() + 1)
// fmt.Printf("to interface: %p %T %v", i, i, i)
//}
func toRoleInterface(i RoleA) {
i.doA()
fmt.Printf("to role interface: i:%p &i:%p", i, &i)
}
type TheImplementation struct {
Value int
}
func (t *TheImplementation) SetValue(i int) {
t.Value = i
}
func (t *TheImplementation) GetValue() int {
return t.Value
}
func (t *TheImplementation) doB() {
t.Value++ // will work as expected (pointer receiver)
fmt.Printf("doB: %p %p %v\n", t, &t, t)
}
func (t TheImplementation) doA() {
t.Value++ // will not work as expected (value receiver)
fmt.Printf("doA: %p %p %v\n", t, &t, t)
}
type TheHeader interface {
RoleB
RoleA
HasValue
}
type RoleA interface {
doA()
}
type RoleB interface {
doB()
}
type HasValue interface {
SetValue(i int)
GetValue() int
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment