Go

Go

Made by DeepSource

Impossible interface nil check GO-W1001

Bug risk
Major

In Go, interface values are type tagged, i.e., they are essentially pairs of the form (value, type), where value is a non-interface value with the given type. Such a pair is never nil, even if the value is nil. So, suppose a non-interface value v is assigned to a variable x whose type is an interface. In that case, x will never be nil. As pair (value, type) is not nil regardless of any value of v (even nil) as the type is present.

Comparing x to nil is pointless, and doing so might indicate a misunderstanding of Go interfaces or some other underlying bug.

Bad practice

// SingerError implements `error` interface
type SingerError struct{}

func (*SingerError) Error() string {
    return ""
}

func sing(song string) (string, *SingerError)

func singer(song string) {
    var m string
    var e error

    m, e = sing(song)
    // NOTE: `e`'s comparison to nil is not correct. It is always
    // non-nil in this case.
    // Reason: `e` is declared to be of type `error`, an interface.
    // As, `*SingerError` type is assigned to `e`'s interface tuple (value,type)
    // as interface values in Go are type tagged, thus, `e` is not `nil`, unless both
    // (value,type) are `nil`. But in this case, even if the value is `nil`, the type
    // is non-nil i.e., `*SingerError`, hence the comparison with `nil` is not correct
    // and `e`` is always `!= nil`.
    if e != nil {
        fmt.Printf("Unable to sing %s: %v
", song, e)
    } else {
        fmt.Printf("Sang %s
", m)
    }
}

Recommended

// SingerError implements `error` interface
type SingerError struct{}

func (*SingerError) Error() string {
    return ""
}

func sing(song string) (string, *SingerError)

func singer(song string) {
    m, e := sing(song)
    // NOTE: Here the comparison with `nil` is correct as in this case,
    // a short variable declaration (operator `:=`) is used, which will automatically
    // infer the more precise non-interface type `*ResourceError` for `e`, making
    // the `nil` check work as expected.
    if e != nil {
        fmt.Printf("Unable to sing %s: %v
", song, e)
    } else {
        fmt.Printf("Sang %s
", m)
    }
}

References