Trying to do go koan, i got stuck in understanding the interface(struct) syntax, what exactly does it do ? I came up with following fun program, which has further confused me on how is interface casting working :
package main
import "fmt"
type foo interface{ fn() }
type t struct { }
type q struct { }
func (_i t ) fn() { fmt.Print("t","
") }
func (_i q ) fn() { fmt.Print("q","
")}
func main() {
_j := t{}
_q := q{}
// This is alright ..
fmt.Print( _j.fn,"
") //0x4015e0
fmt.Print( _q.fn,"
") //0x401610
_j.fn() //t
_q.fn() //q
// both pointers same .. why ?
fmt.Print( foo(_j).fn,"
") //0x401640
fmt.Print( foo(_q).fn,"
") //0x401640
// but correct fns called .. how ?
foo(_j).fn() //t
foo(_q).fn() //q
// same thing again ...
_fj := foo(_j).fn
_fq := foo(_q).fn
// both pointers same .. as above
fmt.Print( _fj,"
") //0x401640
fmt.Print( _fq,"
") //0x401640
// correct fns called .. HOW !
_fj() //t
_fq() //q
}
The pointer are what i'm getting my machin, YMMV. My question is .. what exactly does interface(struct) returns ? and how does interface(struct).func , finds the original struct ... is there some thunk/stub magic going on here?
From here: http://research.swtch.com/interfaces
what exactly does
interface(struct)
return?
It creates a new interface value (like the one you see on top in the graphic), wrapping a concrete struct value.
how does
interface(struct).func
find the original struct?
See the data field in the graphic. Most of the time this will be a pointer to an existing value. Sometimes it will contain the value itself if it fits, though.
In the itable you'll see a function table (where fun[0] is).
I assume that on your machine 0x401640
is the address of the respective pointers to fn
, which is in that table for foo
. Although this is best verified by someone working on the GC compiler suite.
Note that the behaviour you discovered is not strictly defined to be so. Compiler builders can take other approaches to implementing Go interfaces if they like to, as long as the language semantics are preserved.
Edit to answer questions in the comments:
package main
import "fmt"
type foo interface {
fn()
}
type t struct{}
type q struct{}
func (_i t) fn() { fmt.Print("t", "
") }
func (_i q) fn() { fmt.Print("q", "
") }
func main() {
_j := t{}
_j1 := t{}
fmt.Println(foo(_j) == foo(_j)) // true
fmt.Println(foo(_j) == foo(_j1)) // true
}
On the diagram you see 3 blocks:
The one on the left side labeled Binary is a concrete type instance, like your struct instances _j
and _j1
.
The one on the top center is an interface value, this one wraps (read: points to) a concrete value.
The block on the right lower side is the interface definition for Binary underlyings. This is where the jump table / call forwarding table is (itable).
_j
and _j1
are two instances of the concrete type t
. So there are two of the lower-left blocks somewhere in memory.
Now you decide to wrap both _j
and _j1
in interfaces values of type foo
; now you have 2 of the top-center blocks somewhere in memory, pointing back at _j
and _j1
.
In order for the interface value to remember what its underlying type is and where the methods of those types are it keeps a single instance of the lower-right block in memory, to which both interface values for _j
and _j1
respectively point to.
In that block you have a jump table to forward method calls made on the interface values to the concrete underlying type's implementation. That's why both are the same.
It's worth mentioning that unlike Java and C++ (not sure about Python), all Go methods are static and the dot-call notation is only syntactic sugar. So _j
and _j1
don't have different fn
methods, it's the same exact method called with another implicit first parameter which is the receiver on which the method is called.