Last active
October 27, 2021 09:41
-
-
Save wodor/5ef65c1cc82119059fbad82ed5bea8b5 to your computer and use it in GitHub Desktop.
Demonstrates how pointers work for methods with interfaces as parameters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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