Skip to the content.

Pitfall

compare error with nil

A function shall return an error interface instead of a specific error type. Otherwise, its user is easy to fall into a pitfall.

“The Go Programming Language Specification”:

Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

package main

import (
        "fmt"
)

type appErr string
func (e *appErr) Error() string {
        return string(*e)
}

// suggest to return an error interface
// instead of a specific error type
func login() *appErr {
        return nil 
}

func start() error {
        return login()
}

func main() {
        // fall into a pitfall when comparing with nil
        // output: error: *main.appErr, <nil>
        // although the user expects "no error"
        if err := start(); err != nil {
                fmt.Printf("error: %T, %v\n", err, err)
        }

        fmt.Println(login() == nil) // output: true
        fmt.Println(start() == nil) // output: false

        // the underlying principle:
        // err1 is a pointer whereas err2 is an interface
        var err1 *appErr = nil 
        var err2 error = err1

        fmt.Println(err1 == nil) // output: true
        fmt.Println(err2 == nil) // output: false

        fmt.Printf("%T, %v\n", err1, err1) // output: *main.appErr, <nil>
        fmt.Printf("%T, %v\n", err2, err2) // output: *main.appErr, <nil>
}

compare two interfaces

The user shall check comparable before comparing two interfaces.

“The Go Programming Language Specification”:

A comparison of two interface values with identical dynamic types causes a run-time panic if that type is not comparable.

This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.


        var a, b int
        var i, j any

        i, j = a, b
        fmt.Println(i == j) // output: true

        s1, s2 := []int{1}, []int{1}
        // compile error:
        //    invalid operation: s1 == s2 (slice can only be compared to nil)
        // fmt.Println(s1 == s2)

        i, j = s1, s2
        // run-time error:
        //    panic: runtime error: comparing uncomparable type []int
        // fmt.Println(i == j)

        c1 := reflect.TypeOf(s1).Comparable()
        c2 := reflect.TypeOf(s2).Comparable()
        // output: false false
        // fmt.Println(c1, c2)
        if c1 && c2 {
                fmt.Println(i == j)
        }