Last active
March 18, 2020 20:22
-
-
Save mtilson/34d8c8bb106bc42d4b737537cac1a62e to your computer and use it in GitHub Desktop.
confusion related to interface satisfaction of types for using 'values' VS 'pointers to value' as type method receivers [golang]
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" | |
) | |
type InterfaceType interface { | |
MethodReceiverIsPtr() | |
MethodReceiverIsValue() | |
} | |
type ConcreteType int | |
func (ct ConcreteType) MethodReceiverIsValue() { fmt.Println(ct) } | |
func (ct *ConcreteType) MethodReceiverIsPtr() { *ct++ } | |
// actually two different concrete types declared above | |
// 1. `ConcreteType` - has only one method: | |
// 1. `MethodReceiverIsValue()` | |
// 2. `*ConcreteType` - has two methods: | |
// 1. `MethodReceiverIsPtr()` | |
// 2. `MethodReceiverIsValue()` | |
// - pointer types acquire the methods on the corresponding value | |
// - it is legal to call a *T method on an argument of type T so long as the argument is a variable - syntactic sugar | |
// !!! NOTE: the same variable can be used (treated) as these different types depending on usage expressions (`var` and `*var`) | |
func main() { | |
// ========= implicit type casting (the compiler implicitly takes the address of type T for *T method receiver) | |
var ct0 ConcreteType = ConcreteType(0) | |
ct0.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `ct0.MethodReceiverIsPtr()` is IMPLICITLY treated as `(&ct0).MethodReceiverIsPtr()` | |
ct0.MethodReceiverIsValue() // treated type: ConcreteType // output: 1 | |
var ct1, ct2 ConcreteType // both initialized as ConcreteType(0) | |
ct1.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `ct1.MethodReceiverIsPtr()` is IMPLICITLY treated as `(&ct1).MethodReceiverIsPtr()` | |
(&ct1).MethodReceiverIsValue() // treated type: *ConcreteType // output: 1 | |
(&ct2).MethodReceiverIsPtr() // treated type: *ConcreteType | |
(&ct2).MethodReceiverIsValue() // treated type: *ConcreteType // output: 1 | |
var ct3 *ConcreteType = new(ConcreteType) // returns a pointer to a zero value of type ConcreteType | |
ct3.MethodReceiverIsPtr() // treated type: *ConcreteType | |
ct3.MethodReceiverIsValue() // treated type: *ConcreteType // output: 1 | |
var ct4 *ConcreteType = nil; _ = ct4 | |
// ct4.MethodReceiverIsPtr() // runtime error: `invalid memory address or nil pointer dereference` | |
// ct4.MethodReceiverIsValue() // runtime error: `invalid memory address or nil pointer dereference` | |
// ========= implicit type casting is not applied for interface satisfaction | |
var it1 InterfaceType = new(ConcreteType) // dynamic type: *ConcreteType | |
it1.MethodReceiverIsPtr() // `*ConcreteType` has `MethodReceiverIsPtr()` method | |
it1.MethodReceiverIsValue() // `*ConcreteType` has `MethodReceiverIsValue()` method // output: 1 | |
var ctX ConcreteType = ConcreteType(0) | |
var it2 InterfaceType = &ctX // dynamic type: *ConcreteType | |
it2.MethodReceiverIsPtr() // `*ConcreteType` has `MethodReceiverIsPtr()` method | |
it2.MethodReceiverIsValue() // `*ConcreteType` has `MethodReceiverIsValue()` method // output: 1 | |
var it3 InterfaceType = nil; _ = it3 // dynamic type: nil // dynamic value: nil | |
// it3.MethodReceiverIsPtr() // runtime error: `invalid memory address or nil pointer dereference` | |
// it3.MethodReceiverIsValue() // runtime error: `invalid memory address or nil pointer dereference` | |
// var it4 InterfaceType = ConcreteType(0) // dynamic type: ConcreteType | |
// compile error: `ConcreteType does not implement InterfaceType | |
// (MethodReceiverIsPtr method has pointer receiver)` | |
// | |
// `ConcreteType` does not satisfy `InterfaceType` interface as | |
// `ConcreteType` has `MethodReceiverIsValue()` method, | |
// but it does not have `MethodReceiverIsPtr()` method | |
// ========= getting CONCRETE TYPE value from empty interface and implicit type casting | |
var ei1 interface{} = ConcreteType(0) // dynamic type: ConcreteType // dynamic value: ConcreteType(0) | |
var var1 ConcreteType = ei1.(ConcreteType) // `var1` is of type `ConcreteType` returned by interface TYPE ASSERTION TO CONCRETE TYPE | |
var1.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `var1.MethodReceiverIsPtr()` is IMPLICITLY treated as `(&var1).MethodReceiverIsPtr()` | |
var1.MethodReceiverIsValue() // treated type: ConcreteType // output: 1 | |
var ei2 interface{} = ConcreteType(0) // dynamic type: ConcreteType // dynamic value: ConcreteType(0) | |
var2 := ei2.(ConcreteType) // `var2` is of type `ConcreteType` returned by TYPE ASSERTION TO CONCRETE TYPE | |
var2.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `var2.MethodReceiverIsPtr()` is IMPLICITLY treated as `(&var2).MethodReceiverIsPtr()` | |
var2.MethodReceiverIsValue() // treated type: ConcreteType // output: 1 | |
var ei3 interface{} = ConcreteType(0) // dynamic type: ConcreteType // dynamic value: ConcreteType(0) | |
if var3, ok := ei3.(ConcreteType); ok { // `var3` is of type `ConcreteType` SURELY returned by interface TYPE ASSERTION TO CONCRETE TYPE | |
var3.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `var3.MethodReceiverIsPtr()` is IMPLICITLY treated as `(&var3).MethodReceiverIsPtr()` | |
var3.MethodReceiverIsValue() // treated type: ConcreteType // output: 1 | |
} | |
// ========= getting INTERFACE TYPE from empty interface and getting `interface conversion` runtime error | |
// TODO | |
var ei4 interface{} = ConcreteType(0) // dynamic type: ConcreteType // dynamic value: ConcreteType(0) | |
var var4 ConcreteType = ei1.(ConcreteType) // `var1` is of type `ConcreteType` returned by interface TYPE ASSERTION TO CONCRETE TYPE | |
var1.MethodReceiverIsPtr() // treated type: *ConcreteType | |
// `var1.MethodReceiverIsPtr()` is implicitly treated as `(&var1).MethodReceiverIsPtr()` | |
var1.MethodReceiverIsValue() // treated type: ConcreteType // output: 1 | |
// Does not work | |
//var ei2 interface{} = i | |
//kUnboxed := k.(Incrementor) // panic: interface conversion: main.I is not main.Incrementor: missing method Increment | |
//kUnboxed.Increment() | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment