In this article, we will understand how to use ==
the operator in Go to compare object values. We will also look at scenarios where the behaviour of this operator looks like a bug in the language but it is due to a lack of understanding.
Let's go through the below example
var a *string = nil
var b interface{} = a
fmt.Println("a == nil:", a == nil) // true
fmt.Println("b == nil:", b == nil) // false
fmt.Println("a == b: ", a == b ) // true
To understand the above example lets start with a simple one:
var a int = 12
var b int = 12
c:= 12
fmt.Println("a == b :", a == b) // true
fmt.Println("a == c :", a == c) // true
This is very obvious.
Let's twist the example
var a *string = nil
var b interface{} = nil
fmt.Println("a == nil:", a == nil) // true
fmt.Println("b == nil:", b == nil) // true
fmt.Println("a == b:", a == b) // false (even though the value is
nil for both a and b)
To understand the last case, let us dive deeper into it.
In Go, every pointer variable has two values associated with it <type, value>
The fact that every variable needs a type is the reason why we can not have a nil value assigned to a variable whose type is not defined. That's why we can not write x := nil
because we don't mention the type of x and we get the following error use of untyped nil
Now let's see our problem again
var a *string = nil // a is <*string, nil>
var b interface{} = nil // b is <nil, nil>
fmt.Println("a == nil:", a == nil) // true
fmt.Println("b == nil:", b == nil) // true
fmt.Println("a == b:", a == b) // false <*string,nil>!=<nil, nil>
In our case variable a
actually represent <*string, nil>
and interface{}
default type is nil
so variable b
is <nil, nil>
.
So that's why when we say a == b
then we are actually comparing <*string,nil> == <nil, nil>
which is false.
Now come back to our main example
var a *string = nil // a is <*string, nil>
var b interface{} = a
fmt.Println("a ==nil:",a==nil)//true(<*string, nil>==<*string, nil>)
fmt.Println("b == nil:", b == nil) //false (<*string,nil>==<nil,nil> fmt.Println("a == b: ", a == b ) // true
When we do a == nil
, ==
the operator compares type as well as value. Here the value is nil for both LHS and RHS, but what will be the type of nil ???
When nil(hard-coded value) is compared with an object, the type of nil is the same as the declaration type of object with whom it is compared.
So in the case of a == nil
we are doing <*string, nil> == <*string, nil>
In the case of b == nil
, RHS is <nil, nil>
because the declaration type of b
is interface{} which is nil (default). Hence when we did
var b interface{} = a
Then we assigned the value of a
to variable b
which means b
now refers to <*string, nil>
. Hence LHS != RHS
Note: var b interface{} = a
does not change the declaration type of b
variable.
Conclusion
This issue occurs frequently in industrial code like below code snippet is very common in Go where we can face such error.
var resp string
var err error
resp, err = CallFunction()
if err != nil{
// code inside this if statement will be executed if return type of CallFunction doesn't match with error
}
func CallFunction() custom.Error{
// return pointer variable
}
Here is the playground for more experimentation https://play.golang.org/p/BnPPXEs9_Oq
So it's always better to check that the return type of called function and type of receiving variables in the caller function should match.