Grozz
Grozz

Reputation: 8425

Golang pass nil value as an interface through reflection

I have a function with interface argument:

func f(e error) {
    if e == nil {
        fmt.Println("YEY! NIL") // how to get here?
    } else {
        fmt.Println("NOT NIL :(")
    }
}

How do I pass it a nil value via reflect so that it passes == nil check?

Approach 1:

func main() {
    rf := reflect.ValueOf(f)

    nilArg := reflect.Zero(reflect.TypeOf((error)(nil))) // panic: reflect: Zero(nil)

    rf.Call([]reflect.Value{nilArg})
}

Approach 2:

type MyError struct{}
func (e MyError) Error() string {
    return ""
}

func main() {
    rf := reflect.ValueOf(f)
    nilArg := reflect.Zero(reflect.TypeOf(&MyError{})) // NOT NIL :(

    rf.Call([]reflect.Value{nilArg})
}

Second approach doesn't work due to https://golang.org/doc/faq#nil_error

Playground: https://play.golang.org/p/V0bMSPcCKI

Upvotes: 3

Views: 2668

Answers (2)

Thundercat
Thundercat

Reputation: 120999

Use the expression reflect.TypeOf((*error)(nil)).Elem() to get the reflect.Type for interface error.

The first approach in the question does not work because the expression reflect.TypeOf((error)(nil)) returns nil. The concrete type of a nil interface value is nil.

The trick is to pass a non-interface value to reflect.TypeOf() and use reflect methods to get the desired reflect.Type from there. In this answer, I pass a *error to reflect.TypeOf() and call Elem on the result to get the reflect.Type for error.

Use the following to create nilArg:

nilArg := reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())

playground example

Upvotes: 6

Zak
Zak

Reputation: 5898

You have two options, basically e in the arguments to function f is (*MyError)(nil) not nil. So either assert the interface back to type MyError or use more reflection to check if it's nil.

e.(*MyError) == nil

or

reflect.ValueOf(e).IsNil()

Runnable Example: https://play.golang.org/p/2KWguSx618

useful links:

Upvotes: 0

Related Questions