mirror of https://github.com/grafana/grafana.git
59 lines
1.8 KiB
Go
59 lines
1.8 KiB
Go
package util
|
|
|
|
import "reflect"
|
|
|
|
// IsInterfaceNil checks if an interface is nil or holds a nil value.
|
|
//
|
|
// This function addresses the Go "nil interface" gotcha where an interface
|
|
// can be != nil but still hold a nil value of a specific type.
|
|
//
|
|
// The Problem:
|
|
// In Go, an interface value consists of two parts: a type and a value.
|
|
// An interface is only considered nil when both parts are nil.
|
|
// However, if you assign a typed nil (e.g., (*MyType)(nil)) to an interface,
|
|
// the interface becomes != nil even though it holds a nil value.
|
|
//
|
|
// Example of the gotcha:
|
|
//
|
|
// var p *int = nil // p is a nil pointer
|
|
// var i interface{} = p // i holds a typed nil (*int)(nil)
|
|
// fmt.Println(i == nil) // prints: false (this is the gotcha!)
|
|
// fmt.Println(IsInterfaceNil(i)) // prints: true (correctly identifies nil)
|
|
//
|
|
// Common scenario with error interfaces:
|
|
//
|
|
// func doSomething() error {
|
|
// var err *MyError = nil
|
|
// if someCondition {
|
|
// err = &MyError{msg: "failed"}
|
|
// }
|
|
// return err // returns interface{} containing (*MyError)(nil)
|
|
// }
|
|
//
|
|
// if err := doSomething(); err != nil { // this check fails!
|
|
// // This code won't run even when err contains nil
|
|
// }
|
|
//
|
|
// if err := doSomething(); !IsInterfaceNil(err) {
|
|
// // This correctly identifies the nil error
|
|
// }
|
|
//
|
|
// Supported types: Ptr, Slice, Map, Func, Interface
|
|
// Unsupported nilable types: Chan, UnsafePointer (these return false even when nil)
|
|
//
|
|
// See more about this Go gotcha at:
|
|
// https://go.dev/doc/faq#nil_error
|
|
// https://medium.com/@moksh.9/go-gotcha-when-nil-isnt-really-nil-ddf632720001
|
|
func IsInterfaceNil(i interface{}) bool {
|
|
iv := reflect.ValueOf(i)
|
|
if !iv.IsValid() {
|
|
return true
|
|
}
|
|
switch iv.Kind() {
|
|
case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Func, reflect.Interface:
|
|
return iv.IsNil()
|
|
default:
|
|
return false
|
|
}
|
|
}
|