go 如何優雅的判斷變數是否為 nil

big_cat發表於2024-11-01

如何優雅的判斷變數是否為空呢?如下兩個方法即可(先給著急用答案)

func IsNil(val interface{}) bool {
    if nil == val {
        return true
    }
    return reflect.ValueOf(val).IsNil()
}

func IsNotNil(val interface{}) bool {
    return !IsNil(val)
}

nil 判斷在 go 的日常開發編碼中是比較常見的,不管是錯誤返回,亦是未初始化的 SliceMapChanPointerFuncinterface{} 都會涉及到判空的場景。

func main() {
    var sli []int
    fmt.Println("slice:", sli == nil, reflect.TypeOf(sli).Kind(), reflect.ValueOf(sli).IsValid())

    var ma map[string]int
    fmt.Println("map:", ma == nil, reflect.TypeOf(ma).Kind(), reflect.ValueOf(ma).IsValid())

    var ch chan int
    fmt.Println("chan:", ch == nil, reflect.TypeOf(ch).Kind(), reflect.ValueOf(ch).IsValid())

    var user *struct {
        Name string
        Age  uint8
    }
    fmt.Println("pointer:", user == nil, reflect.TypeOf(user).Kind(), reflect.ValueOf(user).IsValid())

    var fun func() string
    fmt.Println("func:", fun == nil, reflect.TypeOf(fun).Kind(), reflect.ValueOf(fun).IsValid())

    var err error
    //fmt.Println(err == nil, errors.Is(err, nil))
    fmt.Println("error:", err == nil, reflect.TypeOf(err), reflect.ValueOf(err).IsValid())

    var itf interface{}
    fmt.Println("interface{}:", itf == nil, reflect.TypeOf(itf), reflect.ValueOf(itf))
}
slice: true slice true
map: true map true
chan: true chan true
pointer: true ptr true
func: true func true
error: true <nil> false
interface{}: true <nil> <invalid reflect.Value>

註解說明一下

func IsNil(val interface{}) bool {
    // type && value 都為 nil 的場景 nil error / interface{}
    // type nil 則 value 必 nil, 此時可直接與 nil 比較
    // 等價於 nil == reflect.TypeOf(val)
    if nil == val {
        return true
    }
    // 其它型別的變數 需要看 value
    return reflect.ValueOf(val).IsNil()
}

func IsNotNil(val interface{}) bool {
    return !IsNil(val)
}

需要注意的是:為了相容所有型別的變數的判空(我們的目的就是判是否為空),所以引數型別為 interface{},但 ”空介面“變數 並不能簡單的使用 nil == val 來判斷是否為空,因為 interface{} 有兩個屬性 typevalue。只有當 typenil && valuenil 時,才可以直接使用 nil == val 來做判空,而非 interface{} 型別的變數轉為此型別時,type 會被設為對應的資料原型,故此時我們需要進一步判斷 value 是否為空。

例如:

  1. 當我們將一個 空指標 var p *int 傳值給 interface{} 時會得被轉為如下狀態:{type: pointer, value: nil},此時因為 typepointer,所以直接與 nil 等值比較會得 false,應該進一步比較 value 是否為 nil 來得到期望結果,故需要透過 reflect 獲取 value 進行值的判空。
  2. error 型別實為 interface { Error() string },當宣告瞭一個變數 var err error 時,其實值還是一個 {type: nil,value: nil}interface{},可以使用 reflect.Typeof(err), reflect.Valueof(err) 來具體檢視。

引申:
當我們使用 reflect 來反射解析變數獲取 TypeValue 時,一定要先判斷 reflect.Typeof(val)reflect.Valueof(val).IsValid()== nil 來判斷是否為 nil 值的 interface{},如果是則沒有反射的必要了。

func main() {
    var val error
    if nil == val {
        fmt.Println("val is nil:", true)
    }
    if nil == reflect.Typeof(val) {
        fmt.Println("val is nil:", true)
    }
    if !reflect.Valueof(val).IsValid() {
        fmt.Println("val is nil:", true)
    }
    if reflect.Valueof(val).IsValid() {
        fmt.Println("val is nil:", reflect.Valueof(val).IsNil())
        fmt.Println("val is zero:", reflect.Valueof(val).IsZero())
    }
}

相關文章