首页 > 其他分享 >golang 断言

golang 断言

时间:2023-01-04 14:12:50浏览次数:62  
标签:itab 断言 value golang 接口 类型 os

开心一刻

       小学时,因为淘气,再加上成绩不好,爸妈经常打我,有次爷爷奶奶来家里,看到我挨打,心疼啊,就把我接过去住!没一个月,我就被送回父母家了,爷爷奶奶进门第一句话就是: “你们先把这臭小子狠狠地打一顿,我们再回去!”……

写在前面

       golang 的类型断言(Type Assertion)是一个使用在接口值上的操作,用于检查接口类型变量所持有的值是否实现了期望的接口或者具体的类型。

golang 断言语法

       断言的语法格式如下:

value, ok := x.(T)

       其中,x 表示一个接口的类型,这里又可以分为空接口和非空接口两种,T 表示断言的目标类型,可以是具体类型,也可为非空接口类型(不能是空接口的原因应该是断言成空接口没有意义),因此共有四种组合。该断言表达式会返回 x 断言后的值(也就是 value,如果断言失败,value 就是 T 的默认值)和一个布尔值(也就是 ok),可根据该布尔值判断 x 是否为 T 类型。

       接下来针对这四种组合进行一一解析。

空接口.(具体类型)

       该断言比较简单,会判断空接口的动态类型(是一个_type类型的指针,_type就是某种具体类型的类型原数据)是否指向该具体类型 T的类型元数据,如果是,则断言成功,ok 为 true,value 的值为空接口中动态类型的值,否则为 false,value 为类型 T 的默认值。需要注意的一点是:空接口的动态类型和具体类型 T 的类型原数据一致才算断言成功,T 和*T 拥有不同的类型原数据,所以如果空接口的动态类型为*T,具体类型为 T,那么一定断言失败。

举例:

package main

import (
	"fmt"
	"os"
)

func main() {
	var a interface{}
	f, _ := os.Open("test.txt")
	a = f
	value, ok := a.(*os.File)
	fmt.Printf("value type %T, assert result: %t\n", value, ok)
}

       *os.File是一种具体类型,f 恰好是*os.File类型的变量,f 赋值给 a 之后,a 的动态类型为*os.File,动态值为 nil,因此该断言是成功的。注意如果括号中的*os.File改成 os.File,那么断言一定失败。

空接口.(非空接口)

       这种断言会判断空接口的动态类型是否实现了该非空接口 T 要求实现的方法,如果实现了 T 的方法,那么断言成功。这时会将空接口的动态类型和动态值包装为 T 的动态值和动态方法,赋值给 value,这时 value 就可以使用非空接口的方法了。

举例:

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	var a interface{}
	f, _ := os.Open("test.txt")
	a = f
	value, ok := a.(io.ReadWriter)
	fmt.Printf("value type %T, assert result: %t\n", value, ok)
}

       具体判断的方法如下:

  • 当目标类型为非空接口时,首先去根据 <io.ReadWriter,*os.File> 去 itabTable 里面查找对应的 itab 指针。
    • 如果找到了,也要进一步确认 itab.fun[0] 是否等于 0,如果不等于 0那么皆大欢喜这个类型实现了接口的方法,等于 0 则没有实现,断言就失败了。
    • 没有找到,再去检查动态类型的方法列表进行一对一的比较。

为什么会需要去进一步确认 itab.fun[0] 是否等于 0?
因为通过方法列表确定某个具体类型没有实现指定接口,就会把 itab 这里的 fun[0] 置为 0,然后同样会把这个 itab 结构体缓存起来,和那些断言成功的 itab 缓存一样。
这样子的目的就是避免再遇到同种类型断言时重复检查方法列表的操作。

非空接口.(具体类型)

       程序会根据非空接口的接口类型和具体类型的组合去检查 itab 缓存,如果发现该组合与非空接口的 itab 类型的指针一致,则断言成功,否则则失败。

举例:

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	var a io.ReadWriter
	f, _ := os.Open("test.txt")
	a = f
	value, ok := a.(*os.File)
	fmt.Printf("value type %T, assert result: %t\n", value, ok)
}

       这里判断方式比较简单:判断 iface.tab 是否等于 <io.ReadWriter,*os.File> 这个组合对应的 itab 指针。

       不过这里我觉得比较非空接口的动态类型的类型元数据和该具体类型的类型元数据是否一致即可,

非空接口.(非空接口)

       这里就需要去查找 itab 缓存了,以 x.(T) 说明,按照<T的接口类型,x 的动态类型>组成的 itab 指针查找 itab 缓存,如果存在,则去判断 fun[0] == 0是否成立,如果成立,表明x 的动态类型没有实现 T 的接口类型要求实现的方法,也就是断言失败,如果不成立表示 x 的动态类型实现了 T 的接口类型要求实现的方法,那么断言成功。

举例:

package main

import (
	"fmt"
	"io"
	"os"
)

func main() {
	var a io.ReadWriter
	f, _ := os.Open("test.txt")
	a = f
	value, ok := a.(io.Read)
	fmt.Printf("value type %T, assert result: %t\n", value, ok)
}

总结

       如果 T 是具体某个类型,类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功,类型断言返回的结果是 x 的动态值,其类型是 T。
       如果 T 是非空接口类型,类型断言会检查 x 的动态类型是否满足 T。如果检查成功,x 的动态值不会被提取,返回值是一个类型为 T 的接口值。
       无论 T 是什么类型,如果 x 是 nil 接口值,类型断言都会失败。

       这四种的判断断言的方式可以分成两种:

       查看当前接口的动态类似是否满足目标对象。断言目标类型是具体类型,无论是空接口还是非空接口,其实都是看的接口的动态类型是否就是目标类型。
       先查表找到 itab 结构体,找不到比较方法,缓存起来。当目标类型是非空接口,其实判断的方法就是先去根据 <接口类型,动态类型> 组合 去查表,如果找到了,那就是满足了要求,如果没有找到,那么就得比较方法列表,然后缓存起来。

参考

标签:itab,断言,value,golang,接口,类型,os
From: https://www.cnblogs.com/xingyu666/p/17022207.html

相关文章

  • golang的mutex互斥锁
    什么是计算机的锁?以前经常遇到锁的时候,计算机的锁到底是一个普通变量,还是一个数据总线的一个开关。网上查,一上来都是一大推的云里雾里专业术语。看了也不懂,怪本人计算机知识......
  • 官方golang包管理神器,值得一试!
    这是一篇很短的文章,诉说着高效的包管理工具​​gomod​​我们上次说过如何让一个项目在​​Goland​​编译器跑起来,但是要自己去下包,要花不少时间找包下包,是不是很麻烦?......
  • xml转golang结构
     直接上代码xmlSrc:=[]byte(`<xml><abc>123</abc></xml>`)varxXMLxml.Unmarshal(xmlSrc,&x)ifx.Abc=="123"{fm......
  • golang:关于interface{}==nil 的坑及原理分析
    先看一个demopackagemainimport"fmt"typeTestStructstruct{}varts*TestStructfuncgetTestStruct()interface{}{fmt.Println(ts==nil)returnts}funcmain()......
  • Postman 断言和返回数据提取
    Postman接口测试一、环境变量和全局变量1.1环境变量环境变量是用来标识不同的测试环境的,例如可以有沙箱环境、预生产环境和生产环境。在postman中,可以通过创建环境变......
  • Go-22 Golang中空接口和类型断言结合使用的细节
    packagemainimport"fmt"//Golang中空接口和类型断言使用细节typeADDressstruct{ Namestring Phoneint}funcmain(){ varuserinfo=make(map[strin......
  • go-dongle 0.2.4 版本发布,一个轻量级、语义化的 golang 编码解码、加密解密库
    dongle是一个轻量级、语义化、对开发者友好的Golang编码解码和加密解密库Dongle已被awesome-go收录,如果您觉得不错,请给个star吧github.com/golang-module/dong......
  • Go-21 Golang接口详解
    packagemainimport"fmt"//Golang中的接口详解/* 1.接口的介绍 2.Golang接口的定义 3.空接口 4.类型断言 5.结构体值接收者和指针接收者实现接口的区别 6.一......
  • golang中使用原子操作监听配置更新
    配置及代码文件{"name":"sasuke","age":25,"gender":"male","score":99.5}develop.jsonpackagemainimport("crypto/md5""enco......
  • golang写入influxdb2,共3种方式,小心有坑!
    ==事件背景==项目中需要接收设备上报的时序数据,写入到Influxdb中,在压力测试过程中发现服务器CPU占用的非常高,使用pprof解析了CPU占用的详细信息之后,发现Influxdb的AddFie......