kevinyan815 / gocookbook

go cook book
MIT License
789 stars 167 forks source link

接口的类型转换和类型断言 #47

Open kevinyan815 opened 3 years ago

kevinyan815 commented 3 years ago

类型转换

接口的类型转换指的是将具体类型转换为接口类型,在前面说过,将具体类型的值赋值给接口类型的变量、传递给接口类型的函数形参以及通过接口类型的返回值返回时都会发生类型检查,一旦具体类型的值已实现了接口,就会被隐式转换为接口类型的值。

比如下面这个例子有三处发生类型检查,通过后就会把值的类型转换为接口类型。

func main() {
    var rpcErr error = NewRPCError(400, "unknown err") // 赋值给变量 rpcErr 时进行类型检查,看是否实现了 error 接口
    err := AsErr(rpcErr) // 给函数传递参数时进行类型检查
    println(err)
}

func NewRPCError(code int64, msg string) error {
    return &RPCError{ // 函数返回值时进行类型检查
        Code:    code,
        Message: msg,
    }
}

func AsErr(err error) error {
    return err
}

如果值的类型转换成了带方法的接口,对应的运行时结构为 runtime.iface 。iface 的 Data 字段会指向原始值。itab 字段则会记录接口与原始类型的关系, itab.hash 则保存了原始类型的哈希值,可以在做类型断言时快速判断目标类型跟原始类型是否一致。同理如果把值转换为空接口 interface{} 类型, 其对应的运行时结构为 runtime.eface.。 eface 的 Data 字段指向原始值,_type 字段则是存储了值原始类型的信息。

类型断言

上面类型转换介绍是如何把具体类型转换成接口类型,而这一节介绍的是如何将一个接口类型转换成具体类型。

首先来看一下 Go 语言类型断言的语法。

类型断言提供对一个接口值底层具体值的访问

t := i.(T)

这个语句断言接口值 i 保存了一个具体类型T的值,并把底层类型为 T 的值赋给变量 t

如果 i 保存的不是一个 T 类型的值,这个语句将会引发 panic。为了避免类型断言引发的 panic ,Go 提供了一直测试类型断言的语法,这个语法有点类型于判断 Map 中是否存在指定键的语法。

t, ok := i.(T)

如果断言失败,ok 的值会是 false 不会引发程序崩溃。

除此之外,Go 还提供了 type-switch 语法:

switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

类型断言发生在编译期间,生成的汇编指令会比较接口类型 runtime.iface 或者 runtime.eface (空接口用 eface 类型表示,否则用 iface) 里保存的原始类型 hash (iface.hash 或者 eface._type.hash)与要目标类型 hash 是否相等,相等则是断言成功,将接口值转换成具体类型的值