What does this print, and why? errors.New("x") == errors.New("x")
Question 295MediumGo 1.22 to 1.25
a := errors.New("x")
b := errors.New("x")
fmt.Println(a == b) // false
fmt.Println(a.Error() == b.Error()) // true
type codeErr struct{ code int }
func (e codeErr) Error() string { return strconv.Itoa(e.code) }
var c, d error = codeErr{1}, codeErr{1}
fmt.Println(c == d) // true
errors.New returns a *errorString, a pointer. Two interface values are equal when their dynamic types and dynamic values are equal. Here the values are two different pointers, so each errors.New call creates a unique identity. That is why sentinel errors such as io.EOF work: you compare against the one package-level variable.
With a value type (codeErr), equality compares fields, so two separately created errors are ==. That may be what you want, but it can surprise you. It also means a sentinel of a struct value type can be "forged" by any caller.
Gotcha: never compare errors by their Error() string. Messages are for humans and can change.
More on Error Handling & panics
- Q294Why does Go treat errors as values instead of using exceptions? What are the trade-offs?
- Q296What is the difference between
%wand%vinfmt.Errorf? - Q297How does
errors.Iswork internally, and how can a custom type change its matching? - Q298Explain
errors.As. Why must the target be a pointer, and what happens if you get it wrong? - Q299What is
errors.Join? How do multi-errors interact witherrors.Is,errors.Asanderrors.Unwrap? - Q300Compare sentinel errors, typed errors and opaque errors. When would you use each?