尝试去koan,我无法理解接口(struct)语法,究竟是什么 它做到了吗? 我提出了以下有趣的程序,这进一步让我对界面转换如何工作感到困惑:
package main
import "fmt"
type foo interface{ fn() }
type t struct { }
type q struct { }
func (_i t ) fn() { fmt.Print("t","\n") }
func (_i q ) fn() { fmt.Print("q","\n")}
func main() {
_j := t{}
_q := q{}
// This is alright ..
fmt.Print( _j.fn,"\n") //0x4015e0
fmt.Print( _q.fn,"\n") //0x401610
_j.fn() //t
_q.fn() //q
// both pointers same .. why ?
fmt.Print( foo(_j).fn,"\n") //0x401640
fmt.Print( foo(_q).fn,"\n") //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,"\n") //0x401640
fmt.Print( _fq,"\n") //0x401640
// correct fns called .. HOW !
_fj() //t
_fq() //q
}
指针是我得到的机器,YMMV。 我的问题是..接口(struct)究竟返回了什么? 接口(struct).func如何找到原始结构... 这里有一些thunk / stub魔法吗?
答案 0 :(得分:3)
从这里开始:http://research.swtch.com/interfaces
interface(struct)
究竟返回了什么?
它创建一个新的界面值(就像你在图形顶部看到的那样),包装一个具体的结构值。
interface(struct).func
如何找到原始结构?
请参阅图形中的数据字段。大多数情况下,这将是指向现有值的指针。但是,如果它适合,它有时会包含值本身。
在 itable 中你会看到一个功能表(其中 fun [0] 是)。
我假设您的计算机0x401640
是指向fn
的相应指针的地址,该表位于foo
的表中。虽然这是GC编译器套件工作人员最好的验证。
请注意,您发现的行为并未严格定义为如此。只要保留the language semantics,编译器构建器就可以采用其他方法来实现Go接口。
编辑以回答评论中的问题:
package main
import "fmt"
type foo interface {
fn()
}
type t struct{}
type q struct{}
func (_i t) fn() { fmt.Print("t", "\n") }
func (_i q) fn() { fmt.Print("q", "\n") }
func main() {
_j := t{}
_j1 := t{}
fmt.Println(foo(_j) == foo(_j)) // true
fmt.Println(foo(_j) == foo(_j1)) // true
}
在图表上,您会看到3个街区:
左侧标有 Binary 的那个是具体的类型实例,就像你的结构实例_j
和_j1
一样。
顶部中心的那个是一个接口值,这个包裹(读取:指向)具体值。
右下方的块是 Binary 底层的接口定义。这是跳转表/呼叫转发表( itable )的位置。
_j
和_j1
是具体类型t
的两个实例。因此,内存中有两个左下方的块。
现在,您决定将_j
和_j1
包装在foo
类型的接口值中;现在你在内存中的某个位置有两个顶部中心块,指向_j
和_j1
。
为了让接口值能够记住它的底层类型是什么以及这些类型的方法在哪里,它会在内存中保留右下方块的单个实例,两个接口值都是_j
和_j1
分别指向。
在该块中,您有一个跳转表,用于将对接口值进行的方法调用转发给具体底层类型的实现。这就是为什么两者都是一样的。
值得一提的是,与Java和C ++(不确定Python)不同,所有Go方法都是静态的,而点调符号只是语法糖。因此_j
和_j1
没有不同的fn
方法,它与使用另一个隐式第一个参数调用的方法相同,该参数是调用该方法的接收器。