Comparison in Golang
Table of Contents
Context⌗
Recently I stumbled upon an issue where a comparison operator was not behaving the way I supposed it would behave.
import "fmt"
func IsEmpty(s interface{}) bool {
return s == ""
}
type AliasString string
var s string
var s2 AliasString
func main() {
fmt.Println(IsEmpty(s)) // true
fmt.Println(IsEmpty(s2)) // I Expected: true, Returned: false
fmt.Println(s2 == "") // Based on above should be false as well, but returns true
}
When this happend the first thought that came in my mind was this.
The issue we were facing was similar, let me explain.
Go Spec⌗
To understand why this happens let’s read though how Go Spec specifies Comparison Operators
Assignable⌗
The spec says:
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
Now let’s create an example to test if an empty string (""
) is assignable to our AliasString
type.
package main
import "fmt"
type AliasString string
func main() {
var s String = ""
fmt.Println(s)
}
Looks like the code runs fine which means it’s assignable. Let’s look elsewhere now.
Comparable⌗
In our IsEmpty
method we are accepting interface{}
and comparing it to an empty string. So, let’s only focus on the part of the spec that specifies that.
Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
Let’s learn more about identical.
Identical⌗
From the spec,
Two types are either identical or different.
A defined type is always different from any other type
Now these two statement looks to be of interest for us. There are also some examples presented in spec which sheds some more light it.
From spec
type (
A0 = []string
A1 = A0
A2 = struct{ a, b int }
A3 = int
A4 = func(A3, float64) *A0
A5 = func(x int, _ float64) *[]string
)
type (
B0 A0
B1 []string
B2 struct{ a, b int }
B3 struct{ a, c int }
B4 func(int, float64) *B0
B5 func(x int, y float64) *A1
)
type C0 = B0
B0 and B1 are different because they are new types created by distinct type definitions; func(int, float64) *B0 and func(x int, y float64) *[]string are different because B0 is different from []string.
Ok based on that it means:
AliasString != string
Conclusion⌗
So, based on all we have read, no two defined types are equal in Golang and hence empty AliasString
!= empty string
. But the last time works because the go compiler doesn’t treat (""
) as empty string
but treats it as empty AliasString
and thus they are equal.
Shout-Outs⌗
Many thanks to Rohit Sudebi, Gregor Best and Joe Davidson for helping me with understand this problem.