0

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()
    }
    

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

So I have to admit that the problem is just a bad solution on my side. Your answer aligns with my second option in code. It is also a suggested in the Go FAQ: go.dev/doc/faq#nil_error Thanks :) I have chosen to fix it with the first option in my real world case to avoid converting concrete to interface in the first place and was the easiest way out at the moment.
0

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))

3 Comments

Yes it wont panic, but in my example I want to avoid the call to e.Message() in the first place. That's why the If is there. Burak's answer I think is more valid since it aligns with the suggestions on this in GO FAQ: go.dev/doc/faq#nil_error
But thanks for explanation. It helps to understand things bit more.
@Klamber Thanks, that's fine. I upvoted Burak's answer because it directly answered your question. But I felt it was useful to respond to the "panic" part of your question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.