go语言函数如何传递interface
2017-09-07 本文已影响30人
CodingCode
如何传递interface
package main
import (
"fmt"
)
type MyInterface interface {
foo()
}
type MyStruct struct {
i1 int64
}
func (m MyStruct) foo() {
fmt.Println("i1:", m.i1)
}
func Hello1(p interface{}) {
}
func Hello2(p MyInterface) {
p.foo()
}
func main() {
var i int
var m MyStruct
Hello1(i)
Hello1(m)
Hello2(m)
}
我们看main函数如何调用hello1和hello2的
Hello1(i)
Hello1(i)
47c055: 48 8b 44 24 28 mov 0x28(%rsp),%rax # move i value to %rax
47c05a: 48 89 44 24 40 mov %rax,0x40(%rsp) # copy i value to 0x40(%rsp) as parameter
47c05f: 48 8d 05 3a e8 00 00 lea 0xe83a(%rip),%rax # 48a8a0 <type.*+0xd8a0>
47c066: 48 89 04 24 mov %rax,(%rsp) # move i type address to 0(%rsp)
47c06a: 48 8d 44 24 40 lea 0x40(%rsp),%rax # move 0x40 + %rsp (i.e., copy i address) to %rax
47c06f: 48 89 44 24 08 mov %rax,0x8(%rsp) # move copy i address to 9(%rax)
47c074: e8 f7 f8 f8 ff callq 40b970 <runtime.convT2E> # call runtime.convT2E, empty interface
47c079: 48 8b 44 24 18 mov 0x18(%rsp),%rax
47c07e: 48 8b 4c 24 10 mov 0x10(%rsp),%rcx
47c083: 48 89 0c 24 mov %rcx,(%rsp) # prepare interface parameter in (%rsp), pointer to type information
47c087: 48 89 44 24 08 mov %rax,0x8(%rsp) # prepare interface parameter in (%rsp), pointer to value
47c08c: e8 5f f8 ff ff callq 47b8f0 <main.Hello1>
这个逻辑很清楚,变量i是一个整数,Hello1接收一个interface,这就需要把i转变成interface,然后再调用Hello1,这个转变的过程就由runtime.convT2E/convT2I来完成。
系统函数convT2E/convT2I的定义如下(根据是interface还是empty,分别调用)。
函数 runtime.convT2E,通常负责将一个变量换成 empty interface,即interface{},函数convT2I将负责把一个变量换成一个指定的non-empty interface,比如在我们例子中MyInterface。
convT2E/convT2I都是返回一个interface对象,这个对象包含两个指针共16字节,一个指向变量的类型信息地址,一个指向变量的值地址。
func convT2E(t *_type, elem unsafe.Pointer) (e eface) {
...
x := newobject(t)
typedmemmove(t, x, elem)
e._type = t
e.data = x
return
}
func convT2I(tab *itab, elem unsafe.Pointer) (i iface) {
t := tab._type
...
x := newobject(t)
typedmemmove(t, x, elem)
i.tab = tab
i.data = x
return
}
Hello1(m)
Hello1(m)和Hello1(i)是一样的过程。
Hello1(m)
47c091: 48 8b 44 24 20 mov 0x20(%rsp),%rax # move m.i1 value to %rax
47c096: 48 89 44 24 38 mov %rax,0x38(%rsp) # copy m.i1 value to 0x38(%rsp) as parameter
47c09b: 48 8d 05 9e 73 01 00 lea 0x1739e(%rip),%rax # 493440 <type.*+0x16440>
47c0a2: 48 89 04 24 mov %rax,(%rsp)
47c0a6: 48 8d 44 24 38 lea 0x38(%rsp),%rax
47c0ab: 48 89 44 24 08 mov %rax,0x8(%rsp)
47c0b0: e8 bb f8 f8 ff callq 40b970 <runtime.convT2E>
47c0b5: 48 8b 44 24 10 mov 0x10(%rsp),%rax
47c0ba: 48 8b 4c 24 18 mov 0x18(%rsp),%rcx
47c0bf: 48 89 04 24 mov %rax,(%rsp)
47c0c3: 48 89 4c 24 08 mov %rcx,0x8(%rsp)
47c0c8: e8 23 f8 ff ff callq 47b8f0 <main.Hello1>
Hello2(m)
Hello2(m)和Hello1(m)也大体类似,除了使用convT2I去转换一个具体的interface类型。
Hello2(m)
47c0cd: 48 8b 44 24 20 mov 0x20(%rsp),%rax
47c0d2: 48 89 44 24 30 mov %rax,0x30(%rsp)
47c0d7: 48 8d 05 02 b1 07 00 lea 0x7b102(%rip),%rax # 4f71e0 <go.itab.main.MyStruct,main.MyInterface>
47c0de: 48 89 04 24 mov %rax,(%rsp)
47c0e2: 48 8d 44 24 30 lea 0x30(%rsp),%rax
47c0e7: 48 89 44 24 08 mov %rax,0x8(%rsp)
47c0ec: e8 2f f9 f8 ff callq 40ba20 <runtime.convT2I> # here is the difference with interface, convT2I vs. convT2E
47c0f1: 48 8b 44 24 10 mov 0x10(%rsp),%rax
47c0f6: 48 8b 4c 24 18 mov 0x18(%rsp),%rcx
47c0fb: 48 89 04 24 mov %rax,(%rsp)
47c0ff: 48 89 4c 24 08 mov %rcx,0x8(%rsp)
47c104: e8 27 fb ff ff callq 47bc30 <main.Hello2>
有一个关于interface的介绍文档供参考
https://research.swtch.com/interfaces