interface comparison in Go

By Gavin     @2020-07-04     965 views

A colleague found error comparison failed him a day, and he asked me for help. He showed me his code and there’s something like this.

type MyErr struct {
}

// *MyErr implemented error interface
func (e *MyErr) Error() string {
	return "My Error"
}

// we do something here and return a *MyErr as an error
func getError() error {
	var err *MyErr
	return err
}

There’s a *MyErr implemented error interface, and getError() was defined to return an error interface. Let’s try some comparison.

println(getError() == nil) // guess what? there's a false

WTF! Go is failing me! But ….. Wait, let’s try something

println(getError().(*MyErr) == nil) //Ahhhh, it's a true

So maybe we were wrong. There must be something we don’t know about the interface comparison. And I found something I didn’t notice before in FAQ of go, Why is my nil error value not equal to nil? And the link is below if you’re interested.

https://golang.org/doc/faq#nil_error

Two interfaces equal if both type and value are equal. Error we returned in getError() is a *MyErr type with value nil. It’s not equal to nil because the type is *MyErr. Let’s try other types

var x, y interface{}
println(x == y) // true, both nil
x = int32(1)
y = int64(1)
println(x == y) // false, type int32 != int64
x = int64(1)
println(x == y) // true, both are int64 (type) and 1 (value)

Guess that’s it. But how to avoid bugs like this? We can always return nil instead of a variable to avoid interface comparison bugs.

func getError() error {
  if errOccur {
    return SomeErr // return a variable only if there was an error
  }
  return nil // always return nil if there's no error
}

And you can check out the test code here on GitHub.

Gavin's Daily

© 2019-2020 Gavin's GoBlog