what exactly is interface(struc) and interface(struc).function

huangapple go评论74阅读模式
英文:

what exactly is interface(struc) and interface(struc).function

问题

尝试解决一个问题时,我在理解接口(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
}

指针是我在我的机器上得到的,可能会有所不同。我的问题是,接口(struct)到底返回什么?
接口(struct)。func 如何找到原始的结构体...
这里是否有一些 thunk/stub 魔法?

英文:

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","\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	       
}

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?

答案1

得分: 3

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

what exactly is interface(struc) and interface(struc).function

> interface(struct) 究竟返回什么?

它创建一个新的接口值(就像你在图中看到的顶部那个),包装了一个具体的结构体值。

> interface(struct).func 如何找到原始的结构体?

请看图中的 data 字段。大多数情况下,这将是一个指向现有值的指针。有时,如果适合的话,它将包含值本身。

itable 中,你会看到一个函数表(其中 fun[0] 位于其中)。

我假设在你的机器上,0x401640 是指向 fn 的相应指针的地址,该指针在 foo 的表中。尽管最好由 GC 编译器套件的开发人员进行验证。

请注意,你发现的行为并没有严格定义为这样。如果编译器构建者愿意,他们可以采用其他方法来实现 Go 接口,只要保留语言语义即可。

<hr>

编辑以回答评论中的问题:

package main

import &quot;fmt&quot;

type foo interface {
	fn()
}

type t struct{}
type q struct{}

func (_i t) fn() { fmt.Print(&quot;t&quot;, &quot;\n&quot;) }
func (_i q) fn() { fmt.Print(&quot;q&quot;, &quot;\n&quot;) }

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 的接口值中;现在,在内存中有 2 个顶部中心的块,分别指向 _j_j1

为了使接口值记住其底层类型以及这些类型的方法在哪里,它在内存中保留了一个__单一__的右下方块的实例,两个 _j_j1 的接口值分别指向它。

在该块中,你有一个跳转表,用于将在接口值上进行的方法调用转发到具体底层类型的实现。这就是为什么它们都是相同的。

值得一提的是,与 Java 和 C++(不确定 Python)不同,所有的 Go 方法都是静态的,点调用符号只是语法糖。所以 _j_j1 没有不同的 fn 方法,它们调用的是完全相同的方法,只是隐含的第一个参数不同,该参数是调用方法的接收器。

英文:

From here: http://research.swtch.com/interfaces

what exactly is interface(struc) and interface(struc).function

> 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.

<hr>

Edit to answer questions in the comments:

package main

import &quot;fmt&quot;

type foo interface {
	fn()
}

type t struct{}
type q struct{}

func (_i t) fn() { fmt.Print(&quot;t&quot;, &quot;\n&quot;) }
func (_i q) fn() { fmt.Print(&quot;q&quot;, &quot;\n&quot;) }

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.

huangapple
  • 本文由 发表于 2013年10月16日 13:17:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/19395749.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定