关于 nil 的一些事情

关于 nil 的一些事情

本质

首先,nil 没有 type,并且,nil 也不是关键字 而是 预声明的标示符。

其次,在 Go 语言中有以下几种类型可以取值 nil:

示例

接下来,我们来看一段代码,看看 go 语言中对 nil 判定的坑:

package nils

import (
    "net"
    "reflect"
    "testing"
)

type TSlice []string

type IErrNil interface {
    IErrNil() IErrNil
    Error() string
}

type ErrNil struct {
    msg string
}

func (t *ErrNil) Error() string {
    return t.msg
}
func (t *ErrNil) ErrNil() *ErrNil {
    return nil
}
func (t *ErrNil) IErrNil() IErrNil {
    return nil
}
func (t *ErrNil) PrintMsg() string {
    if t == nil {
        return "<nil>"
    }
    return t.msg
}
func (t ErrNil) PrintMsgV2() string {
    return t.msg
}

func Test_Nil(t *testing.T) {
    var err error

    var t ErrNil
    t.PrintMsg()
    t.PrintMsgV2()

    tmp := ErrNil{}
    err = tmp.ErrNil()
    if err == nil {
        t.Logf("(ErrNil == nil) ok: %v, err: %v", err == nil, err)
    } else {
        t.Errorf("(ErrNil == nil) err: %v, err: %v, type(err): %v", err == nil, err, reflect.TypeOf(err).Kind())
    }

    err = tmp.IErrNil()
    if err == nil {
        t.Logf("(IErrNil == nil) ok: %v, err: %v", err == nil, err)
    } else {
        t.Errorf("(IErrNil == nil) err: %v, err: %v, type(err): %v", err == nil, err, reflect.TypeOf(err).Kind())
    }

    ip := net.ParseIP("111.1.111")
    if ip == nil {
        t.Logf("(ip == nil) ok: %v, err: %v, type(ip): %v", ip == nil, ip, reflect.TypeOf(ip).Kind())
    } else {
        t.Errorf("(ip == nil) err: %v, err: %v, type(ip): %v", ip == nil, ip, reflect.TypeOf(ip).Kind())
    }
}

会发现两个问题: 1. t.PrintMsg() 可以执行 但是 t.PrintMsgV2() 会崩溃 2. 第二个判定竟然 err != nil 3. 第三个判断竟然 ip == nil

第一个问题

t.PrintMsg(): 当我们用一个空指针类型的变量(如,var t *ErrNil)调用此方法时,该方法是会执行的,只有在执行该空指针变量的解指针操作(t.msg)时,才会 panic。

t.PrintMsgV2(): 由于接受者是 ErrNil 而不是 ErrNil,使用指针访问该函数时,Golang 内部会在调用时自动解指针,故使用空指针类型的变量(如,var t ErrNil)调用此方法时会 panic。

关于第二个问题:

简单说,interface 被两个元素 value 和 type 所表示。只有在 value 和 type 同时为 nil 的时候,判断 interface == nil 才会为 true。具体可以参考官方文档

关于第三个问题:

这就涉及到 slice 的使用问题。

golang 中的类型可以是基本类型,如:int、float、bool、string;结构化的(复合的),如:struct、array、slice、map、channel;只描述类型的行为的,如:interface。

结构化的类型没有真正的值,它使用 nil 作为默认值。简单的说,[]interface{} 是一个指向 具体 slice 类型对象的指针。所以,可以用 nil 进行赋值 和 判断。

Last updated