-2

Consider the following (demo https://go.dev/play/p/VrJx-eEij14)

type myError struct {
    err error
}

func (e myError) Error() string { return e.err.Error() }

func main() {
    var (
        err       error = fmt.Errorf("%w", myError{errors.New("I am an error")})
        nilErr    error = nil
        errAny    any   = err
        nilErrAny any   = nilErr
    )

    {
        recErr, ok := errAny.(error)
        fmt.Println(recErr, ok)
    }

    {
        recErr, ok := nilErrAny.(error)
        fmt.Println(recErr, ok)
    }
}

It prints

I am an error true
<nil> false

while I expected it to print

I am an error true
<nil> true

Trying the same exercice replacing the interface type error with *int (demo https://go.dev/play/p/NtHVyYO9Hfj)

func main() {
    var (
        p       *A  = &A{}
        nilp    *A  = nil
        pAny    any = p
        nilpAny any = nilp
    )

    {
        rec, ok := pAny.(*A)
        fmt.Println(rec, ok)
    }

    {
        rec, ok := nilpAny.(*A)
        fmt.Println(rec, ok)
    }
}

does yield to the output I expect

&{} true
<nil> true

Why can one recover a nil int pointer from an empty interface but not a nil error from an empty interface?


FYI, the specific situation I am trying to solve is one, where I am caching errors. The values cached are of type any. Some errors cached are nil. I care about the difference between a nil error and an object that I could unexpectedly not type assert into an error because I would have messed my cache up.

Remi.b
  • 17,389
  • 28
  • 87
  • 168
  • 3
    The assignment `nilErrAny any = nilErr` assigns the value and dynamic type in `nilErr` to `nilErrAny`. The interface type is not assigned. The type assertion `nilErrAny.(error)` fails because `nilErrorAny`'s dynamic type (nil in this case) does not satisfy the `error` interface. – Charlie Tumahai Nov 10 '22 at 23:48

1 Answers1

0

Internally, a variable of interface type is an intermediate object that contains a pointer to the actual object and information about the type of that object, and it is possible for the interface value to be "empty", which is represented by the keyword nil (though this situation is distinct from a nil pointer). Thus, there are two ways in which a variable of interface type can "be nil": the variable itself can be nil (the intermediate object is empty), or the intermediate object contains a pointer to the actual object is nil. Assigning the literal nil to a variable of interface type produces the first situation. You can produce the second situation with e.g. nilErr error = nil.(*myError), or by assigning a nil pointer that is already held in a variable of type *myError.

Aasmund Eldhuset
  • 37,289
  • 4
  • 68
  • 81
  • 2
    Not quite. An interface value is a 2 word value (not intrinsically a pointer), containing the dynamic type and value. The dynamic value may be contained within the interface value or it may be a pointer depending on type and size. This is unspecified though, and can and has changed over time. – JimB Nov 10 '22 at 23:42
  • @JimB: Rephrased to avoid usage of the word "pointer" for the interface value itself. – Aasmund Eldhuset Nov 10 '22 at 23:50