1

I have a classic Go nil interface issue.

I'm trying to assert an interface{}, which I assign from a nil error, back to an error interface. That sentence is confusing so I have a handy-dandy example: https://play.golang.com/p/Qhv7197oIE_z

package main

import (
    "fmt"
)

func preferredWay(i interface{}) error {
    return i.(error)
}

func workAround(i interface{}) error {
    if i == nil {
        return nil
    }
    return i.(error)
}

func main() {
    var nilErr error
    fmt.Println(workAround(nilErr))    // Prints "<nil>" as expected.
    fmt.Println(preferredWay(nilErr))  // Panics.
}

Output:

<nil>
panic: interface conversion: interface is nil, not error

goroutine 1 [running]:
main.preferredWay(...)
    /tmp/sandbox415300914/prog.go:8
main.main()
    /tmp/sandbox415300914/prog.go:21 +0xa0

So in other words, I'm trying to downcast from a nil interface{} to a nil error interface. Is there an elegant way to do this if I know the interface{} was assigned as a nil error to begin with?

FYI, if this seems unusual, it's because I'm implementing some mocking for testing.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
crunk1
  • 2,560
  • 1
  • 26
  • 32
  • 1
    Regardless of the value being `nil` or not, when doing [type assertion](https://golang.org/ref/spec#Type_assertions) use the comma-ok idiom to avoid panics. *(see the last paragraph in the linked section of the documentation)* – mkopriva Sep 04 '19 at 05:06
  • Considering my use case, I don't mind the panics. I'm using it for a mock client so when implement tests, I can set return values for the mock client methods. Having it panic is actually helpful in this case because a panic fails fast and means that my test is implemented incorrectly. – crunk1 Sep 04 '19 at 05:49

1 Answers1

5

Does this work?

func preferredWay(i interface{}) error {
    k, _ := i.(error)
    return k
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Seems like a good way to do it in this case. Though, I am curious about the semantics of type assertions on nil interfaces. Even `var i interface{}; i.(interface{})` panics. – crunk1 Sep 04 '19 at 05:43
  • 1
    In that case, i is nil, so type assertion panics because it looks at the value of the interface, but the value is nil. The v,_:=i.(interface{}) will not panic because if checks first if i can be converted to an interface. If i was a non-nil interface with nil value, then the type assertion would not fail. – Burak Serdar Sep 04 '19 at 05:51
  • 2
    @ScottCrunkleton This has little to do with the fact that the value is `nil`, the problem is that the `nil` is *untyped*, i.e. the type of the "non-value" stored in `i` is not `interface{}`, is not anything, and so `i.(interface{})` fails, and without `, ok` it panics. Try giving `i` a *typed* `nil`, and then asssert `i` to that type, does it still panic? No it doesn't. https://play.golang.com/p/_xFk0jX5YIW – mkopriva Sep 04 '19 at 06:01
  • 4
    An interface has a pair of references, a reference to a type and reference to a value. If the interface itself is nil, then these are both nil. If the interface points to a nil value, then the type is non-nil (it points to the type of the nil value), but the value is nil. If the interface is of the first kind, type assertion fails because type is nil. If the interface is of the second kind, type assertion works because there is a non-nil type. – Burak Serdar Sep 04 '19 at 06:09
  • 1
    @bserdar +1 your's is a much more accurate and clear explanation then my attempt. – mkopriva Sep 04 '19 at 06:18