Go 接口实现原理【高阶篇】: type _interface struct

The Internal Definition Of Interface Types

https://www.tapirgames.com/blog/golang-interface-implementation

All interface types have the same internal definition:

type _interface struct {dynamicTypeInfo *_implementationdynamicValue    unsafe.Pointer // unsafe.Pointer means// *ArbitraryType in Go.
}

The internal _implementation type is declared like

type _implementation struct {itype   *_type   // the interface type.dtype   *_type   // the dynamic type, which must implement itype.methods []*_func // the methods which are defined on dtype// and each of them implements a// corresponding method declared in itype.
}

From the definitions, we know that each interface value contains two pointer fields. The dynamicValue field stores the dynamic value information, and the dynamicTypeInfo field stores the implementation information. dynamicTypeInfo.itype stores the type information of the interface value and dynamicTypeInfo.dtype stores the type information of the dynamic value.

The dynamicTypeInfo field of an interface value may be nil, which means nothing is stored in the interface value. For this case, the dynamicValue field must be also nil. We can also say the dynamic value of the interface value is untyped nil for this case.

For the official Go compiler and runtime, a non-nil dynamicValue field value may store

  • the address of the dynamic value if the dynamic type is not a pointer type, or
  • the dynamic value itself if the dynamic type is a pointer type.

Surely, it is not essential to make the exception for pointer dynamic values. This is just a compiler optimization. We can get why it is an optimization in following sections. (BTW, about more current and future optimizations in the official interface implementation, please read this article: http://commaok.xyz/post/interface-allocs/.)

Other involved internal types are declared like:

type _func struct {name      string  methodSig uint // two methods with the same signature have// the same signature id. Receiver parameter// doesn't contribute to this signature.funcSig   uint // receiver parameter accounts to this signature.// other information ...
}type _type struct {name       string   // type nameid         uint32   // each type has unique idflags      uint32   // comparable? isPointer?size       uintptr  // value size in byteskind       uint8    // methods    []*_func // the methods are sorted // by methodSig and name.// more information ...
}const (// flagsTypeFlag_Comparable = 1 << 0TypeFlag_IsPointer  = 1 << 1TypeFlag_IsString   = 1 << 2
)func (t *_type) IsComparable() bool {return t.flags & TypeFlag_Comparable != 0
}func (t *_type) IsPointer() bool {return t.flags & TypeFlag_IsPointer != 0
}func (t *_type) IsString() bool {return t.flags & TypeFlag_IsString != 0
}

Some fields of above types are not listed, for they are unrelated to this article.

Here is the function to get an _implementation value from an interface type and a non-interface type:

// global table
var cachedImpls = map[uint64]*_implementation{}// itype must be an interface type and
// dtype must be a non-interface type.
// Return nil if dtype doesn't implement itype.
// Must not return nil if dtype implements itype.
func getImpl (itype *_type, dtype *_type) *_implementation {var key = uint64(itype.id) << 32 | uint64(dtype.id)var impl = cachedImpls[key]if impl == nil {// for each (dtype, itype) pair, the implementation// method table is only calculated most once at// run time. The calculation result will be cached.var numMethods = len(itype.methods)var methods = make([]*_func, numMethods)// find every implemented methods.// The methods of itype and dtype are both sorted// by methodSig and name.var n = 0var i = 0for _, im := range itype.methods {for i < len(dtype.methods) {tm := dtype.methods[i]i++// Here, for simplicity, assume// all methods are exported.if tm.methodSig < im.methodSig {continue}if tm.methodSig > im.methodSig {// im method is not implementedreturn nil}if tm.name < im.name {continue}if tm.name > im.name {// im method is not implementedreturn nil}methods[n] = tmn++break}}// dtype doesn't implement all methods of itypeif n < numMethods {return nil}// dtype implements itype.// create and cache the implementation.impl = &_implementation{dtype: dtype, itype: itype, methods: methods,}cachedImpls[key] = impl}return impl
}

This function will be called in the value conversions explained in following sections.

In any Go program, at run time, all _implementation values are cached and stored in a global map and all _type values are stored in a global immutable array.

As the blank interface type interface{} is used popular in Go programming, the official Go compiler uses a different and more efficient underlying definition for the blank interface than other interface types:

// blank interface
struct {dynamicType     *_type         // the dynamic typedynamicValuePtr unsafe.Pointer // points to the dynamic value
}

To make the explainations simpler, following sections will treat blank interface types as general interface types.

Convert Non-Interface Values To Interface Types

Here is the internal function to convert a non-interface value to an interface type:

// To call this function, compilers must assure
// 1\. itype is an interface type.
// 2\. dtype is nil or a non-interface type and implements itype.
// p must be nil if dtype is nil.
// p is a pointer stores the address of a value of dtype if dtype
// is not a pointer type. p stores the value of a value of dtype
// if dtype is a pointer type.
func t2i (itype *_type, dtype *_type, p unsafe.Pointer) _interface {// dtype is nil means the non-interface value is untyped nilif dtype == nil {return _interface {dynamicValue:    nil,dynamicTypeInfo: nil,}}// the pointer dynamic value optimization, no need to// allocate the extra memory block.if dtype.IsPointer() {return _interface {dynamicValue:    p,dynamicTypeInfo: getImpl(dtype, itype),}}// for non-pointer dynamic value, runtime must// allocate an extra memory block to store a copy// of the non-pointer value.var t = memoryAlloc(dtype) memoryCopy(t, p, dtype.size)return _interface {dynamicValue:    t,dynamicTypeInfo: getImpl(dtype, itype),}
}

Compilers will insert a call of this function before

  • assigning a non-interface value to an interface value, to convert the non-interface value to the type of the interface value.
  • comparing a non-interface value with an interface value, to convert the non-interface value to the type of the interface value.

Convert Interface Values To Other Interface Types

Here is the internal function to convert an interface value to an interface type:

// To call this function, compilers must assure
// 1\. itype is an interface type.
// 2\. the dynamic value of ivalue is untyped nil
//    or the dynamic type of ivalue implements ivalue.
//    (the method set of the dynamic type of ivalue must
//    must be a super set of the method set of itype).
func i2i (itype *_type, ivalue _interface) _interface {// the dynamic value of ivalue is untyped nil.if ivalue.dynamicTypeInfo == nil {return _interface {dynamicValue:    nil,dynamicTypeInfo: nil,} // <=> return ivalue}// compilers should avoid calling this function// for this case.if ivalue.dynamicTypeInfo.itype == itype {return ivalue // return a copy of ivalue.}// Convert the dynamic value of ivalue to itype.// Here, the returned interface value and ivalue// will share the same extra memory block which// stores the dyanmic value if the dynamic value// is not a pointer.return _interface {dynamicValue:    ivalue.dynamicValue,dynamicTypeInfo: getImpl(ivalue.dynamicTypeInfo.dtype,itype,), // here, the getImpl call never return nil.}
}

Compilers will call this function before

  • assigning an interface value to another interface value, to convert the first interface value to the type of the second interface value.
  • comparing an interface value with another interface value, to convert the first interface value to the type of the second interface value.

Compilers should translate converting an interface value to its own type as a no-op.

Assign Interface Values

In an interface value assignment, the destination value must be an interface value, and the type of the source value must implement the destination interface type. The source value may be either a non-interface value or an interface value. As above two sections mentioned, compilers will convert the source value to the destination interface type before the assignment. So in the final assignment, the source value and the destination value have the same type, the destination interface type.

For the current official Go compiler/runtime, there are just two copies for the two fields, dynamicValue and dynamicTypeInfo, in the final assignment. So if the dynamic value is non-pointer, the underlying dynamic value memory block, which address is stored in the dynamicValue field, will be shared between the destination and source interface values. However, this should be viewed as an optimization. Other compilers may not adopt this optimization.

Compare Interface Values

There are three comparison circumstances involving interface values:

  • interface value vs. interface value.
  • interface value vs. non-interface value.
  • interface value vs. untyped nil.

A good compiler should treat the three circumstances differently to get better program performance. Here, for simplicity, we assume non-interface and untyped nil values will be converted to interface{} type before making the comparisons. So all comparisons involving interface values can be viewed as comparing two interface values.

Here is the internal function to compare two interface values:

func iCompare (ivalue1 _interface, ivalue2 _interface) bool {// untyped nil is only equal to untyped nil.if ivalue1.dynamicTypeInfo == nil {return ivalue2.dynamicTypeInfo == nil}if ivalue2.dynamicTypeInfo == nil {return false}// panic on incomparable dynamic values.if ! ivalue1.dynamicTypeInfo.dtype.IsComparable() {panic(ivalue1.dynamicTypeInfo.dtype.name +" is incomparable")}if ! ivalue2.dynamicTypeInfo.dtype.IsComparable() {panic(ivalue2.dynamicTypeInfo.dtype.name +" is incomparable")}// return false if dynamic type is not the same.if ivalue1.dynamicTypeInfo.dtype != ivalue2.dynamicTypeInfo.dtype {return false}// optimization: early return.if ivalue1.dynamicValue == ivalue2.dynamicValue {return true}// special case: string comparisonif ivalue1.dynamicTypeInfo.dtype.IsString() {return stringCompare(*(*string)(ivalue1.dynamicValue),*(*string)(ivalue2.dynamicValue),)}// general case: compare all bytes in dynamic value// memory blocks one by one.return memoryCompare(ivalue1.dynamicValue,ivalue2.dynamicValue,ivalue1.dynamicTypeInfo.dtype.size,)
}

This article will not explain how two strings are compared.

Type Assert To Non-Interface Types

Here is the internal function to assert an interface value to a non-interface type:

// To call this function, compilers must assure
// 1\. dtype is a non-interface type.
// 2\. outP is nil or stores the address of a value of dtype.
// 3\. outOk is nil or stores the address of a bool value.
func assertI2T (ivalue _interface, dtype *_type,outP *unsafe.Pointer, outOk *bool) {// dynamic value is untype nil.if ivalue.dynamicTypeInfo == nil {// if okay is not present, panic.if outOk == nil {panic("interface is nil, not " + dtype.name)}// return (zero value, false)*outOk = falseif outP != nil {if dtype.IsPointer() {*outP = nil} else {memoryReset(*outP, dtype.size)}}return}// assersion fails.if ivalue.dynamicTypeInfo.dtype != dtype {// if ok is not present, panic.if outOk == nil {panic("interface is " +ivalue.dynamicTypeInfo.dtype.name +", not " + dtype.name)}// return (zero value, false)*outOk = falseif outP != nil {if dtype.IsPointer() {*outP = nil} else {memoryReset(*outP, dtype.size)}}return}// assersion succeeds.if outOk != nil {*outOk = true}if outP == nil {return}// copy dynamic value.if dtype.IsPointer() {*outP = ivalue.dynamicValue} else {memoryCopy(*outP, ivalue.dynamicValue, dtype.size)}
}

Type Assert To Interface Types

Here is the internal function to assert an interface value to an interface type:

// To call this function, compilers must assure
// 1\. itype is an interface type.
// 2\. outI is nil or stores the address of a value of itype.
// 3\. outOk is nil or stores the address of a bool value.
func assertI2I (ivalue _interface, itype *_type,outI *_interface, outOk *bool) {// dynamic value is untype nil.if ivalue.dynamicTypeInfo == nil {// if ok is not present, panic.if outOk == nil {panic("interface is nil, not " + itype.name)}*outOk = falseif outI == nil {*outI = _interface {dynamicValue:    nil,dynamicTypeInfo: nil,}}return}// check whether or not the dynamic type implements itypevar impl = getImpl(itype, ivalue.dynamicTypeInfo.dtype)// assersion fails.if impl == nil {// if ok is not present, panic.if outOk == nil {panic("interface is " +ivalue.dynamicTypeInfo.dtype.name +", not " + itype.name)}// return (zero value, false)*outOk = falseif outI != nil {*outI = _interface {dynamicValue:    nil,dynamicTypeInfo: nil,}}return}// assersion succeeds.if outI == nil {*outOk = true}if outI != nil {*outI = _interface {dynamicValue:    ivalue.dynamicValue,dynamicTypeInfo: impl,}}
}

If the type of the interface value is the asserted interface type, compilers should simply return the interface value.

Call Interface Methods

For an interface value i, a call of its nth method (by the order after sorted)

... = i.Method_n(...)

will be translated to

if i.dynamicTypeInfo == nil {panic("runtime error: nil pointer dereference")
}if i.dynamicTypeInfo.dtype.IsPointer() {... = _call(i.dynamicTypeInfo.methods[n], i.dynamicValue, ...)
} else {... = _call(i.dynamicTypeInfo.methods[n],*(*unsafe.Pointer)(i.dynamicValue), ...)
}

The interfacetype structure

Finally, here's the interfacetype structure (src/runtime/type.go):

type interfacetype struct { // 80 bytes on a 64bit archtyp     _typepkgpath namemhdr    []imethod
}type imethod struct {name nameOffityp typeOff
}

As mentioned, an interfacetype is just a wrapper around a _type with some extra interface-specific metadata added on top.
In the current implementation, this metadata is mostly composed of a list of offsets that points to the respective names and types of the methods exposed by the interface ([]imethod).

Conclusion

Here's an overview of what an iface looks like when represented with all of its sub-types inlined; this hopefully should help connect all the dots:

type iface struct { // `iface`tab *struct { // `itab`inter *struct { // `interfacetype`typ struct { // `_type`size       uintptrptrdata    uintptrhash       uint32tflag      tflagalign      uint8fieldalign uint8kind       uint8alg        *typeAlggcdata     *bytestr        nameOffptrToThis  typeOff}pkgpath namemhdr    []struct { // `imethod`name nameOffityp typeOff}}_type *struct { // `_type`size       uintptrptrdata    uintptrhash       uint32tflag      tflagalign      uint8fieldalign uint8kind       uint8alg        *typeAlggcdata     *bytestr        nameOffptrToThis  typeOff}hash uint32_    [4]bytefun  [1]uintptr}data unsafe.Pointer
}

learn more:https://github.com/teh-cmc/go-internalshttps://github.com/teh-cmc/go-internals/blob/master/chapter2_interfaces/README.mdhttps://research.swtch.com/interfaceshttps://go.dev/doc/effective_go#interfaces_and_typeshttps://www.tapirgames.com/blog/golang-interface-implementation


http://www.taodudu.cc/news/show-5656548.html

相关文章:

  • typescript interface 覆盖
  • 1.1 typescript中的interface
  • 《黑手党:天堂之城》全攻略
  • 新手装机攻略!
  • 《极品飞车 地下狂飙2》秘籍
  • 近期对FTP及Excel数据处理的一些摸索
  • 对于程序员, 为什么英语比数学更重要? 如何学习
  • 主流SpringCloud微服务架构,您可少走弯路
  • 如何快速打好Java基础?
  • 焦点图(轮播图)
  • JavaScript面向对象焦点图片轮播banner
  • jQuery手机触屏左右滑动切换栏目和焦点图
  • web实战(一)— — 焦点图轮播特效
  • Element UI 使用 el-carousel 焦点图
  • ppt流程图字体太小_高质量PPT制作方法分享,缩短你与同事的差距
  • 7年级计算机组成ppt,《第二课_计算机的硬件软件了解计算机系统的组成课件》初中信息技术滇教科课标版七年级全一册课件.ppt...
  • 新生入学计算机课程认知教育PPT,《初识小学信息技术之开学第一课》
  • 一文理解分布式服务架构下的混沌工程实践(含PPT)
  • Word文档的四种加密方法
  • 解读4大谜题 30岁远离皱纹
  • 股票都去哪儿了
  • 脸上皱纹隐藏健康秘密 看哪里的皱纹最危险
  • 警惕!秋季眼部保养的9大误区
  • 如何应对肌肤皱纹和松弛 让你更年轻
  • “顺纹路,单向疏导”抚平皱纹按摩手法
  • 超2000万图片,全球最大人眼图像数据集开源了
  • 80后女孩如何预防皱纹的产生
  • 时间都去哪了
  • 教你如何制造皱纹
  • 人体的五官检测和皱纹检测系统-matlab

Go 接口实现原理【高阶篇】: type _interface struct相关推荐

  1. php redis微信发红包,高阶篇二 使用Redis队列发送微信模版消息

    # 高阶篇二 使用Redis队列发送微信模版消息 > 此命令行执行任务的方法类比较复杂 他需要命令行运行才会有效 > 命令行源码以及创建方法 参见上节 https://www.kanclo ...

  2. 爬虫requests高阶篇详细教程

    文章目录 一.前言 二.SSL验证 三.代理设置 四.超时设置 ​ 五.身份认证 1)基本身份认证 2)摘要式身份认证 六.总结 一.前言 本篇文高阶篇,上一篇为基础篇,希望你一定要学完基础再来看高阶 ...

  3. 高阶篇:4.2.2)DFMEA层级分明的失效模式、失效后果、失效原因

    本章目的:明确失效模式.失效后果.失效原因的定义,分清楚层次关系,完成DFMEA这部分的填写. 1.失效模式,失效后果,失效原因的定义 这是FEMEA手册第四册中的定义. 1.1 潜在失效模式 (b) ...

  4. Redis7实战加面试题-高阶篇(案例落地实战bitmap/hyperloglog/GEO)

    案例落地实战bitmap/hyperloglog/GEO 面试题: 抖音电商直播,主播介绍的商品有评论,1个商品对应了1系列的评论,排序+展现+取前10条记录 用户在手机App上的签到打卡信息:1天对 ...

  5. 高阶篇:4.3)FTA故障树分析法-DFMEA的另外一张脸

    本章目的:明确什么是FTA,及与DFMEA的关系. 1.FTA定义 故障树分析(FTA) 其一:故障树分析(Fault Tree Analysis,简称FTA)又称事故树分析,是安全系统工程中最重要的 ...

  6. Prompt工程师指南[高阶篇]:对抗性Prompting、主动prompt、ReAct、GraphPrompts、Multimodal CoT Prompting等

    Prompt工程师指南[高阶篇]:对抗性Prompting.主动prompt.ReAct.GraphPrompts.Multimodal CoT Prompting等 1.对抗性 Prompting ...

  7. 【檀越剑指大厂--mysql】mysql高阶篇

    文章目录 一.Mysql 基础 1.数据库与实例? 2.mysql 的配置文件 3.mysql 体系结构 4.innodb 的特点? 5.innodb 和 myisam 的区别 6.其他存储引擎? 7 ...

  8. ✨三万字制作,关于C语言,你必须知道的这些知识点(高阶篇)✨

    目录 一,写在前面 二,数据的存储 1,数据类型介绍 2,类型的基本归类 3,整形在内存中的存储 4,浮点型在内存中的存储 三,指针的进阶 1,字符指针 2,指针数组 3,数组指针的使用 4,函数指针 ...

  9. 【檀越剑指大厂—SpringCloudNetflix】SpringCloudNetflix高阶篇

    一.基础概念 1.架构演进 在系统架构与设计的实践中,从宏观上可以总结为三个阶段: 单体架构 :就是把所有的功能.模块都集中到一个项目中,部署在一台服务器上,从而对外提供服务(单体架构.单体服务.单体 ...

最新文章

  1. 数据库开发个人总结(ADO.NET小结)
  2. linux下apache+php+mysql升级安装过程
  3. Bash: Removing leading zeroes from a variable
  4. vue component created没有触发_Vue 全局数据管理-Vuex
  5. 【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流
  6. appium和selenium不同与相同之处
  7. 根深才能叶茂:基础软件突破亟待产业界携手创新完善生态
  8. 动态生成圈形+文字的图片
  9. patterns practices: Mobile Architecture Pocket Guide
  10. zabbix-3.2 官方手册
  11. 深入内核:Oracle数据提交与事务隔离的深度解析
  12. 快速排序的python实现
  13. 功率放大器的性能指标
  14. 为什么c语言没落了,编程语言Go飞速大涨,Delphi走向没落
  15. linux下批量修改图片分辨率
  16. 新版 | 小O地图V0.9.2.0
  17. jQuery动画操作
  18. 解决UglifyJs Unexpected token punc «{», expected pun 报错问题
  19. vcruntime140_1.dll下载及安装【vcruntime140_1.dll丢失的解决方法】
  20. 东京奥运会闭幕式,中国队88枚奖牌收官!

热门文章

  1. C语言offsetof(TYPE, MEMBER)全解
  2. Mine Sweeper
  3. css一元素hover状态控制另一元素显现
  4. 参考文献的期号和卷号
  5. war3_WAR文件与带有嵌入式服务器的Java应用程序
  6. 笔记——Machine-learned Regularization and Polygonizationof Building Segmentation Masks 翻译文本
  7. 青龙面板之小黄鸟抓包
  8. HTML5 LocalStorage 本地存储原理详解
  9. java店铺装修功能_有赞美业店铺装修前端解决方案
  10. 一般报java.lang.NullPointerException的原因有以下几种