空接口作为函数返回值的注意事项

  先来看下面两段代码:

package main

import "fmt"

type someStruct struct{}

func test() any {
   var t *someStruct = nil

   // 重要提醒:函数声明了返回值数据类型为空接口any,但实际返回的却是*someStruct,这时就会发生数据类型转换(*someStruct转
   //           为any),注意这不是隐式转换,Go语言里没有隐式转换,声明了返回any就肯定是返回any,所以属于显式转换。
   return t
}

func main() {
   p := test() // var p any = test()
   if p == nil {
      fmt.Println("p is nil")
   } else {
      fmt.Println("p is not nil") // p is not nil
      fmt.Printf("p = %+v \n", p) // p = <nil>
   }
}

package main

import "fmt"

type someStruct struct{}

func main() {
   var t *someStruct = nil
   p := t // var p *someStruct = t
   if p == nil {
      fmt.Println("p is nil") // p is nil
   } else {
      fmt.Println("p is not nil")
      fmt.Printf("p = %+v \n", p)
   }
}

  首先要知道,第一段代码中的变量p是空接口数据类型,而第二段代码中的变量p是*someStruct数据类型。
 
  第一段代码会让初学者非常困惑,根本原因在于没有理解空接口(即interface{},别名any)的数据结构以及函数返回时发生的数据类型转换。
 
  1、空接口实际上由两部分组成:数据类型和数据类型对应的值,只有两者均为nil时整个空接口才等于nil。
 
  2、test()函数声明了返回值为空接口数据类型,但实际返回值却是*someStruct数据类型,这时就会发生转换,即把*someStruct转为空接口。
 
  明白以上两点后,第一段代码的困惑就很清楚了,由于发生了把*someStruct转为空接口的转换,此时空接口的数据类型为*someStruct,空接口的值为nil。因为空接口的数据类型不为nil,所以整个空接口也不为nil,而fmt.Printf()是打印空接口的值,所以出现了p不等于nil但是又打印出p等于nil的矛盾现象。

  我们可以利用反射来查看空接口的type和value,如下面的代码:

package main

import (
   "fmt"
   "reflect"
)

type someStruct struct{}

func test() any {
   var t *someStruct = nil
   return t
}

func main() {
   p := test()

   reflectType := reflect.TypeOf(p)
   reflectTypeKind := reflectType.Kind()
   reflectTypeKindString := reflectTypeKind.String()
   fmt.Printf("reflectType = %+v \n", reflectType)                     // reflectType = *main.someStruct
   fmt.Printf("reflectTypeKind = %+v \n", reflectTypeKind)             // reflectTypeKind = ptr
   fmt.Printf("reflectTypeKindString = %+v \n", reflectTypeKindString) // reflectTypeKindString = ptr

   reflectValue := reflect.ValueOf(p)
   reflectValueKind := reflectValue.Kind()
   reflectValueKindString := reflectValueKind.String()
   fmt.Printf("reflectValue = %+v \n", reflectValue)                     // reflectValue = <nil>
   fmt.Printf("reflectValueKind = %+v \n", reflectValueKind)             // reflectValueKind = ptr
   fmt.Printf("reflectValueKindString = %+v \n", reflectValueKindString) // reflectValueKindString = ptr

   reflectValueElem := reflectValue.Elem()
   fmt.Printf("reflectValueElem = %+v \n", reflectValueElem) // reflectValueElem = <invalid reflect.Value>

   reflectValueElemKind := reflectValueElem.Kind()
   fmt.Printf("reflectValueElemKind = %+v \n", reflectValueElemKind) // reflectValueElemKind = invalid
}

Copyright © 2024 码农人生. All Rights Reserved