Paperclip
Paperclip

Reputation: 708

Handle pointer to nil pointer in Go

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:

Upvotes: 0

Views: 201

Answers (2)

Erwin Bolwidt
Erwin Bolwidt

Reputation: 31269

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

Upvotes: 0

Burak Serdar
Burak Serdar

Reputation: 51567

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.

Upvotes: 1

Related Questions