Skip to content

Instantly share code, notes, and snippets.

@mtilson
Last active March 18, 2020 20:22
Show Gist options
  • Save mtilson/34d8c8bb106bc42d4b737537cac1a62e to your computer and use it in GitHub Desktop.
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]
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