IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    你会处理 go 中的 nil 吗

    uccs发表于 2023-10-28 16:17:22
    love 0

    对于下面这段代码,我们知道 i 实际上的值就是 nil,所以 i == nil 会生效

    func main() {
      var i *int = nil
      if i == nil {
        fmt.Println("i is nil") // i is nil
      }
    }

    现在换一种写法,我们将 i 的类型改成 interface{},i == nil 依然会生效

    func main() {
      var i interface{} = nil
      if i == nil {
        fmt.Println("i is nil") // i is nil
      }
    }

    我们接着改造,将 i == nil 的逻辑封装成函数 IsNil

    func IsNil(i interface{}) {
      if i == nil {
        fmt.Println("i is nil")
      }
    }
    func main() {
      var i *int = nil
      IsNil(i)
    }

    然后居然发现 IsNil 中的 i == nil 不生效了,为什么呢?

    因为对于 interface{} 类型的值来说,如果要判断它是 nil,必须同时满足 type T 和 value V 都是 nil 才行

    可以用 reflect 中的 TypeOf 和 ValueOf

    var i *int = nil
    fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i)) // *int  <nil>
    
    var i interface{} = nil
    fmt.Println(reflect.TypeOf(i), reflect.ValueOf(i)) // <nil> <invalid reflect.Value>

    但是如果我们在函数中用 interface{} 作为参数的类型,表示并不代表参数就是 interface{} 类型,而是任意类型,调用时传入啥类型就是啥类型,如下代码

    var i interface{} = 1
    fmt.Println(reflect.TypeOf(i)) // int
    
    var j interface{} = "hello"
    fmt.Println(reflect.TypeOf(j)) // string
    
    var k interface{} = nil
    fmt.Println(reflect.TypeOf(k)) // nil

    所以只有当我们传入类型的参数是 interface{} 类型时,且 value 为 nil 时,i == nil 才会生效

    否则其他情况都不会生效

    func main() {
      var i interface{} = nil
      IsNil(i)  // i is nil
    }
    func IsNil(i interface{}) {
      if i == nil {
        fmt.Println("i is nil")
      }
    }

    这个坑可能会出现在返回 error 的函数中,比如下面这段代码

    在函数 SomeThing 中提前定义了 myError,然后一系列的处理后,返回了 myError

    后面的业务逻辑需要判断 err 是否为 nil

    type MyError struct{}
    func (me *MyError) Error() string {
      return "my error"
    }
    func SomeThing() error {
      var myError *MyError    // 默认初始化为 nil
      // ...
      return myError
    }
    func main() {
      err := SomeThing()
      fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // *main.MyError <nil>
      if err != nil {     // 虽然没有返回,这里会被执行,因为 err 的类型不是 nil
        fmt.Println(err)
      }
    }

    从上面的代码我们看到,SomeThing 函数中定义的 myError 是 *MyError 类型,虽然返回了 nil,但是 err 的类型不是 nil,所以 err != nil 会生效,不符合预期

    如果修改这个问题呢,当我们需要返回 nil 时,显示指明返回 nil,如下代码:

    type MyError struct{}
    func (me *MyError) Error() string {
      return "my error"
    }
    func SomeThing() error {
      var myError *MyError    // 默认初始化为 nil
      // ...
      return nil
    }
    func main() {
      err := SomeThing()
      fmt.Println(reflect.TypeOf(err), reflect.ValueOf(err)) // <nil> <invalid reflect.Value>
      if err != nil {     // 这段代码不会被执行
        fmt.Println(err)
      }
    }

    总结:需要返回 nil 时,要显示返回 nil,不要用指针类型的零值



沪ICP备19023445号-2号
友情链接