究竟什么是接口(struc)和接口(struc).function

时间:2013-10-16 05:17:20

标签: go

尝试去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魔法吗?

1 个答案:

答案 0 :(得分:3)

从这里开始:http://research.swtch.com/interfaces

enter image description here

  

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方法,它与使用另一个隐式第一个参数调用的方法相同,该参数是调用该方法的接收器。