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 } }