err != nil can be Disguising
In Go, we see a common pattern to check for errors with err != nil
. This can sometimes be disguising. At VIS, I faced this issue while dealing with a third party library. Let’s see with an example:
In the above example, we have two functions. One function, “chkForOne” takes in a slice of strings and uses the function “isItOne” to check if the string is “1”.
We can see that in both the functions we are returning an error in case the string doesn’t contain a number.
Executing the above will result in the following –
Is it one: true Is it one: false Error: Error while checking for one
No problem anywhere. All good!
Custom Error
error in Go’s standard lib is an interface, which means any struct that implements the method “Error() string” can be treated as an error. Now, instead of standard lib’s errors.New()
let’s go with our own implementation of error interface.
This is a very simple Struct having two string attributes and a method Error() that returns the concatenation of the two attribute values.
Let’s try to use this in the above example
In the function isItOne
we replaced return type from error
to *chkOneErr
and instead of returning errors.New
we created and returned &chkOneErr
The code looks correct and compiles with no errors. However, when we execute it, we get this –
Error: <nil> Error: <nil> Error: Error while checking for one | Value: a
What happened there? Why are we seeing <nil>? Didn’t I put err!=nil
check before I printed?
Well, the real culprit is, the meaning of err != nil
Simplifying The Problem
What do you think will the output be? You probably guessed it wrong. Following is the output:
<nil> <nil> false true
I was surprised too when I found this out (because I didn’t spend time reading Go docs). However, this link provides an explanation.
The way interfaces and struct pointers are evaluated as nil is very different. A struct pointer variable is evaluated nil if it is not pointing to any value. Whereas an interface variable is evaluated as nil if it is not assigned a type and a value. In our case, the interface var has been assigned a type i.e chkOneErr
hence it’s not nil.
Solution: Short variable declaration for errors
Use short variable declaration when assigning error from a function. Avoid pre declaration of error.
Use:
a, err := someFunc()
Instead of:
var err error var a someType
a, err = someFunc()
This way, it doesn’t matter if the function is returning a structPointer
that implements error
interface or an interface
itself. We can always be sure that err != nil
always works as expected.
Our first example would work correctly with this change.