grafana/pkg/util/interface.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
}
}