Consider following simplified example:
package main
import (
"fmt"
)
type IMessenger interface {
Message()
}
type TMyMessenger struct {
}
func (m TMyMessenger) Message() {}
func MessengerFactory() IMessenger {
return getInternalMessengerVariant()
}
func getInternalMessengerVariant() *TMyMessenger {
return nil
}
func main() {
e := MessengerFactory()
fmt.Println(" e == nil", e == nil) // *TMyMessenger(nil)
if e != nil {
e.Message()
}
}
And it's output:
e == nil false
panic: runtime error: invalid memory address or nil pointer dereference
Question 1:
Is there an idiomatic Go way to check if e
points to a nil pointer?
Preferably an inline snippet.
Basically make the e != nil
to be false
even in the example case.
What I have considered:
There would not be this issue if
getInternalMessengerVariant()
would return Interface type instead of concrete pointer, but it requires refactor and may still go undetected and yield itself as a panic at runtime (if e != nil).func getInternalMessengerVariant() IMessenger { return nil }
Rewrite MessengerFactory() to intercept the internal returns:
func MessengerFactory() IMessenger { if m := getInternalMessengerVariant(); m != nil { return m } return nil }
Be very specific on type checking, but what if there are many types:
if e != nil && e != (*TMyMessenger)(nil) { e.Message() }
CodePudding user response:
This problem exists whenever you return an interface from a function: if the interface contains a typed nil-pointer, interface itself is not nil. There is no easy way to check that.
A good way to deal with this is to return a nil for the interface:
func MessengerFactory() IMessenger {
x:= getInternalMessengerVariant()
if x==nil {
return nil
}
return x
}
Then you will not need to check if the return value points to a nil pointer.
CodePudding user response:
Burak Serdar explains well in his answer why if x == nil
returns false
for you.
But that is not the reason why you get the panic.
Go is happy to invoke a receiver function on a nil
pointer, as long as the receiver doesn't dereference the pointer.
This does not panic:
type TMyMessenger struct {
}
func (m *TMyMessenger) Message() {}
func main() {
var t *TMyMessenger = nil
t.Message()
}
and that's because you don't dereference the pointer m
inside the Message
receiver function.
Your example only panics because you have defined the receiver function m
on the type TMyMessenger
(not a pointer). Because of that, Go will have to dereference the nil
pointer to TMyMessenger
that is inside the IMessenger
interface value, in order to invoke the receiver function.
If you change one line in your code, it will no longer panic:
func (m *TMyMessenger) Message() {}
(change (m TMyMessenger)
to (m *TMyMessenger)
)