Go: 了解空接口

Understrand the Empty Interface

- 后端早读课翻译计划署出品 -

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

> 空接口 = 空 interface  = interface{}  , 由于翻译的流程问题,导致同一个定义出现不同的展示。

一个 interface{} 可以包含任何数据,同时他也是一个非常有用的参数,因为他可以是任何的 type。要了解 interface{} 是如何工作的,它是怎样可以符合任何类型的,我们必须要先知道它名字背后的概念。

Interfaces(接口)

这里有一个对空接口很好的定义,by Jordan Oreilli:

interface 包含两种功能:一坨方法的集合,同时他也是一个类型 (type)

interface{} 的类型是一个没有方法的接口。由于它没有需要实现的方法,所以所有的类型都至少实现了零个方法,自然满足了 interface 类型的条件,所有的类型都符合空的 interface

一个使用 Interface{} 类型做参数的方法,可以接收任何类型的参数。Go 会为方法把参数转化为 interface 类型。

Russ Cox 曾写过一篇内部描述 interfaces 的文章,描述了一个 interface 由两部分组成:

  • 一个指针指向存储类型信息

  • 一个指针指向具体的数据

Russ  用 C 语言描述了 interface 的定义

虽然现在的 runtime 使用 Go 写的,但是表述依然是相同的。我们可以通过打印出 interface{} 的指针地址来证明他:

func main() {  var i int8 = 1  read(i)}//go:noinlinefunc read(i interface{}) {  println(i)}print:(0x10591e0,0x10be5c6)

两个指针地址一个指向了类型信息,另一个指向了值。

Underlying structure

空接口的底层描述是在 reflection package 的文档里:

type emptyInterface struct {   typ  *rtype            // word 1 with type description   word unsafe.Pointer    // word 2 with the value}

和上面说的一样,我们可以看到空 interface 有一个描述 type 的指针,以及包含具体数据的 word

rtype 的结构体包含了具体的 type 的描述:

type rtype struct {   size       uintptr   ptrdata    uintptr   hash       uint32   tflag      tflag   align      uint8   fieldAlign uint8   kind       uint8   alg        *typeAlg   gcdata     *byte   str        nameOff   ptrToThis  typeOff}

在这些字段中,有一些我们已知的内容:

  • size 是字节大小

  • kind 包含了 int8, int16, bool 等类型

  • align 是这个类型的变量的对齐方式

根据嵌入到空接口的类型,我们可以映射出字段或者方法:

type structType struct {   rtype   pkgPath name   fields  []structField}

该结构体还有两个包含字段列表的映射关系。它清楚地展示了将内建的类型转换成空接口将会导致一次水平转换,在字段描述和它的值被存在内存中的地方

这是我们看到的空接口的描述:

interface is composed by two words

现在让我们看下,从空接口实际上可以转换到那种类型上。

Conversions

让我们试试用空接口来转换错误的类型

func main() {  var i int8 = 1  read(i)}//go:noinlinefunc read(i interface{}) {  n := i.(int16)  println(n)}

虽然从 int8 转到 int16 是可以的,但是程序会 panic:

panic: interface conversion: interface {} is int8, not int16goroutine 1 [running]:main.read(0x10592e0, 0x10be5c1)main.go:10 +0x7dmain.main()main.go:5 +0x39exit status 2

我们生产 asm 代码来看看 Go 到底检查了什么

code generated while checking the type of an empty interface

步骤如下

  • 步骤 1: 用 int16 (指令 LEAQ:Load Effective Address)的类型与空接口的内部类型(指令 MOVQ:读取空接口 48字节偏移的内存)作比较(指令 CMPQ)

  • 步骤 2:指令 JNE,如果不等于就跳转,跳转到在第 3 步创建的指令,来处理错误信息

  • 步骤 3:代码抛出异常,并生成上一步的错误信息

  • 步骤 4:这是错误指令的结尾。这个特定的指令由显示该指令(main.go:10+0x7d)错误信息所引用

任何从空接口内部类型的转换都是在原始类型转换之后进行。转换成空接口然后再转换回原始类型对你的程序耗时有一些影响。让我们运行一些基准测试来大致了解一下。

性能

这里有两个基准测试。一个是复制一个结构体,另外一个是复制一个空接口。

package main_testimport (  "testing")var x MultipleFieldStructuretype MultipleFieldStructure struct {  a int  b string  c float32  d float64  e int32  f bool  g uint64  h *string  i uint16}//go:noinlinefunc emptyInterface(i interface {}) {  s := i.(MultipleFieldStructure)  x = s}//go:noinlinefunc typed(s MultipleFieldStructure) {  x = s}func BenchmarkWithType(b *testing.B) {  s := MultipleFieldStructure{a: 1, h: new(string)}  for i := 0; i < b.N; i++ {    typed(s)  }}func BenchmarkWithEmptyInterface(b *testing.B) {  s := MultipleFieldStructure{a: 1, h: new(string)}  for i := 0; i < b.N; i++ {    emptyInterface(s)  }}

结果如下

BenchmarkWithType-8               300000000           4.24 ns/opBenchmarkWithEmptyInterface-8      20000000           60.4 ns/op

将类型转换为空接口,再从空接口转换回类型的两次过程消耗量超过 55 纳秒。而且时长会随着结构体内部字段的增加而增加:

BenchmarkWithType-8             100000000         17 ns/opBenchmarkWithEmptyInterface-8    10000000        153 ns/op

但是,用指针转换回相同的结构体指针是一个好办法。转换看起来是下面这个样子

func emptyInterface(i interface {}) {  s := i.(*MultipleFieldStructure)  y = s}

现在结果有显著的不同

BenchmarkWithType-8                 2000000000          2.16 ns/opBenchmarkWithEmptyInterface-8       2000000000          2.02 ns/op

对于像 int 或者 string 这样的基本类型,性能测试略微不同

int:BenchmarkWithTypeInt-8              2000000000          1.42 ns/opBenchmarkWithEmptyInterfaceInt-8    1000000000          2.02 ns/opstring:BenchmarkWithTypeString-8           1000000000          2.19 ns/opBenchmarkWithEmptyInterfaceString-8  50000000           30.7 ns/op

合理并节制使用空接口,在大多数情况下,空接口会对你的程序性能造成一些真正的影响。

点击

go interface类型转换_Go: Understand the Empty Interface相关推荐

  1. go interface类型转换_Go语言的九大核心特性主要有哪些?

    Go语言之所以厉害,是因为它在服务端的开发中,总能抓住程序员的痛点,以最直接.简单.高效.稳定的方式来解决问题.这里给大家讲解一下Go的核心特性. 并发编程 Go语言在并发编程方面比绝大多数语言要简洁 ...

  2. Go interface 类型转换原理剖析

    hi, 大家好,我是 haohongfan. 可能你看过的 interface 剖析的文章比较多了,这些文章基本都是从汇编角度分析类型转换或者动态转发.不过随着 Go 版本升级,对应的 Go 汇编也发 ...

  3. go interface{}类型转换

    go interface{}类型转换 目录 查看interface{}类型 还原interface{}的原类型 1. 查看interface{}类型 func checkType(i interfac ...

  4. golang 接口_「Golang系列」 深入理解Golang Empty Interface (空接口)

    空接口可用于保存任何数据,它可以是一个有用的参数,因为它可以使用任何类型. 要理解空接口如何工作以及如何保存任何类型,我们首先应该理解名称背后的概念. 接口 这是Jordan Oreilli对空接口的 ...

  5. golang interface 类型转换_Golang面试题41道

    Golang面试题41道 大家好,这一期呢,我们来说一下golang的面试题. 第1题什么是golang? go是一个开源的编程语言,由谷歌开发的.这门语言是设计用来做系统级的编程的. 第2题为什么要 ...

  6. golang interface 类型转换_无符号Golang程序逆向方法解析

    在去年的inctf2018中,出现了一道Go语言编写的进程通信逆向题,无论是从题目整体设计还是解题思路上来说都独树一帜,自己在解题过程中遇到了很多问题,但我这不打算做过多探讨,网上也有大佬的解题过程, ...

  7. java interface 函数_java8函数式接口(Functional Interface)

    介绍 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口. 函数式接口可以被隐式转换为 lambda 表达式(箭头函数). 函数式接口代 ...

  8. Go 语言编程 — 高级数据类型 — Interface、多态、Duck Typing 与泛式编程

    目录 文章目录 目录 Golang 的接口 Interface 实例存储的是实现者的值 如何判断某个 Interface 实例的实际类型 Empty Interface Interface 与多态 I ...

  9. golang语言 []interface{}和interface{}

    文章目录 golang语言 []interface{} interface(接口) interface应用场景 interface{} 空接口 []interface{} golang中为什么[]st ...

最新文章

  1. SQL Server中自定义函数和游标应用的经典案例
  2. (0039) iOS 开发之地图之百度vs高德
  3. Imageloader6-mUIHandler的初始化
  4. mysql设置索引树长度_MySQL索引-B+树
  5. linux信号量超过系统限制
  6. Codeforces Round #382 (Div. 2)B. Urbanization 贪心
  7. ACM模板——拓扑排序
  8. MongoDB通配符索引
  9. Error:Unable to make the module: core, related gradle configuration was not found. Please, re-import
  10. poj 2606 Rabbit hunt 解题报告
  11. 日本电子业转型的东芝之困:多年亏损 后有强敌追兵
  12. 如何得到JavaVM,JNIEnv接口
  13. 阿斯蒂芬萨法撒旦法撒旦发射点发射得分萨法撒旦法撒旦法
  14. performSelector延时调用导致的内存泄露
  15. Hive操作——删除表(drop、truncate)
  16. 目标检测算法之AAAI2019 Oral论文GHM Loss
  17. 每日一词20190318——图像金字塔(image pyramid)
  18. python象限判断_玩数据之数据方法篇—四象限分析法—附EXCEL+Python案例
  19. 恒源云(GPUSHARE)_语音识别与语义处理领域之低资源机器翻译综述
  20. [cocos2dx_Lua]动画加速与减速

热门文章

  1. vb.net与c#相互转换工具
  2. SA区坏道数据恢复的经历
  3. 综合布线系统走线槽架的产品选型
  4. 在自行车论坛看到的有趣帖子
  5. 让FX1.1的NotifyIcon支持BalloonTip(1)
  6. Linux-LAMP-访问控制Directory
  7. UI组件-UICollectionView
  8. 中国小品演员都要卷舌?
  9. SPFA算法O(kE)
  10. Subversion快速入门教程