2

Motivation

I have custom error type named CustomError and I want to write a method that parses any type of error into my error structure. So I wrote parse method to show you. I will use the parse method on any function that returns error interface. So all errors will be structured with my type.

Problem

When I use parse method with nil value to return error interface, the returning error is not nil. Code is below. First test succeeded but second did not.

const ( UnHandledError = "UnHandledError" CircuitBreakerError = "CircuitBreakerError" ) type CustomErrorType struct { Key string Message string Status int } func (c *CustomErrorType) Error() string { return "Custom error message" } func parse(err error) *CustomErrorType { if err == nil { return nil } if e, ok := err.(*CustomErrorType); ok { return e } if e, ok := err.(hystrix.CircuitError); ok { return &CustomErrorType{CircuitBreakerError, e.Message, http.StatusTooManyRequests} } return &CustomErrorType{UnHandledError, err.Error(), http.StatusInternalServerError} } func TestParse_it_should_return_nil_when_error_is_nil(t *testing.T) { result := parse(nil) if result != nil { t.Error("result is not nil") } } func TestParse_it_should_return_nil_when_error_is_nil_2(t *testing.T) { aFunc := func() error { return parse(nil) } result := aFunc() if result != nil { t.Error("result is not nil") } } 

Can you explain what am I missing or what is wrong?

1

1 Answer 1

2

This is an instance of a common "problem" of go's interfaces that is caused by the actual implementation of interfaces under the hood: an interface containing a nil pointer is not-nil.

it is described in go's faq with an example that resembles your situation with the error interface: Why is my nil error value not equal to nil?

Under the covers, interfaces are implemented as two elements, a type T and a value V. V is a concrete value such as an int, struct or pointer, never an interface itself, and has type T.

...

An interface value is nil only if the V and T are both unset, (T=nil, V is not set), In particular, a nil interface will always hold a nil type. If we store a nil pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (T=*int, V=nil). Such an interface value will therefore be non-nil even when the pointer value V inside is nil.

This situation can be confusing, and arises when a nil value is stored inside an interface value such as an error return:

func returnsError() error { var p *MyError = nil if bad() { p = ErrBad } return p // Will always return a non-nil error. } 

this example is similar to what is happening in your code when you do:

aFunc := func() error { return parse(nil) } 

parse() returns *CustomErrorType, but the function just error making the value returned an interface that contains a type and a nil value: (T=*CustomErrorType, V=nil) that in turn evaluates to not-nil.

the faq then goes on providing explanation and showing a "correct" example:

If all goes well, the function returns a nil p, so the return value is an error interface value holding (T=*MyError, V=nil). This means that if the caller compares the returned error to nil, it will always look as if there was an error even if nothing bad happened. To return a proper nil error to the caller, the function must return an explicit nil:

func returnsError() error { if bad() { return ErrBad } return nil } 

the behavior can also be observed in your example adding a

fmt.Printf("%#v\n", result) 

to print result's value:

(*e.CustomErrorType)(nil) 

if we change parse() return type to just error it will print:

<nil> 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.