关于go的interface机制

一篇速记

1. 基本实现

照例先贴两个链接吧(好的,我就是粘贴怪了)
Go Data Structures: Interfaces
golang的interface剖析
可以先看第一篇,再看第二篇。
总结一下两篇文章的大意吧

  • interface结构占16个字节(64位机器),前8位是一个tab指针,指向全局的itable(hash表)的一个tab表项(具体的结构体定义可以参考第二篇链接)。后8位是一般是一个数据指针,指向实际存放的数据。
  • 映射关系:(interface接口,实际的数据类型) -> tab表项,tab表项是在运行期间动态计算的(当然不会在编译时来算,因为大部分的tab表项对应的映射关系在程序中是用不到的),具体来说,下面这行代码
var2 := (interface{})(var1)

会陷入runtime(我猜测一种实现的方法是,在编译的时候把这条语句编译为一个runtime的函数调用之类的。)
runtime则负责查找hash表中是否有(interface{}, typeof(var1))对应的tab表项,如果找到了,var2的tab指针就指向该表项,否则就新建一个tab表项,加入到hash表中。

  • 第一点中说到一般是指向数据的指针是因为如果数据本身大小不超过8字节,就没有必要额外去申请堆内存,然后存一个指向堆数据的指针,而直接把数据本身copy到8字节里就可以了

2. 进一步讨论

在展开接下来的讨论前,我再贴一个链接。
Golang method with pointer receiver [duplicate]
具体来说,当我们定义type im struct后,定义方法有两种情况

type iface interface{
	add()
}
func (i im) add() {
//***** method 1
}
func (i *im) add(){
//***** method 2
}

var a iface
a = im{}    // 接口赋值情况1
a = &im{}   // 接口赋值情况2

如果我们采用method 1的定义方法,会发现两种接口赋值均能够通过编译。但是如果采用method 2的定义方法,发现只有接口赋值情况2能够通过编译。
根本原因在于指针类型会继承原类型的方法,但是原类型不能够继承指针类型的方法。一种猜测的实现方法,比如上面的method1可以在编译时生成一个对偶的指针方法。

func (i *im) add{
	(*i).add()
}

这样理解也符合go的语法

func (i im) add{
// **** method 1
}
var h := &im{}
h.add()

虽然我们并没有显式地用*h,通常是认为译器自动地加上了*号,不过这也可以认为是指针类型继承了原类型的add方法,h.add() -> (*h).add()只不过是编译器的内联优化罢了。
另外这样理解也与第一节中的第一个链接中强调的(*Binary).string的调用和(Binary).string的调用区别相吻合。
这样解决了采用method1的定义方法,接口赋值2能够通过编译的问题,那为什么不类似地整一个为原类型准备的对偶函数呢?例如下面这样

func (i *im) add(){
//***** method 2
}
func (i im) add(){   // method2的对偶函数
	(&i).add()
}

好吧,首先这个与go的语法就没有之前那样吻合了,因为当我们写i.add()时希望的是达到传引用的效果,如果是调用上面的对偶函数,会先进行一波copy,传引用的语义就消失了。另外呢,这样的对偶函数失去了传引用语义后,原有add函数的行为的表现与预期可能并不相符(比如添加了这个后也没办法解决stackoverflow上那老哥的问题)。

不过上面的解释只是帮助理解interface的语义,具体编译器也不一定是怎么做的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值