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!
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
*chkOneErr and instead of returning
errors.New we created and returned
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.
a, err := someFunc()
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.