Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a method on a nil struct pointer doesn't panic. Why not?

Tags:

struct

go

type MyError struct {
    errors []string
}

func (t *MyError) Error() string {
    if t == nil {
        fmt.Println("t ptr empty")
        return ""
    }
    pointers := make([]string, 0, len(t.errors))
    for i, r := range t.errors {
        pointers[i] = r
    }
    sort.Strings(pointers)
    return fmt.Sprintf("n error(s) decoding:\n\n%s", len(t.errors), strings.Join(pointers, ","))
}

func main() {
    var err *MyError
    err.Error()  // expected "panic: runtime error: invalid memory address or nil pointer dereference" here
}

The variable err is nil so calling err.Error() method is expected to cause a panic "runtime error: invalid memory address or nil pointer dereference", but the method call succeeds. Why doesn't this panic?

like image 242
cdh0805010118 Avatar asked Feb 15 '17 00:02

cdh0805010118


2 Answers

Michael Jones explained this well (copied as answer):

In Go the function to be called by the Expression.Name() syntax is entirely determined by the type of Expression and not by the particular run-time value of that expression, including nil.

In this manner, the invocation of a method on a nil pointer of a specific type has a clear and logical meaning.

Those familiar with vtable[] implementations will find this odd at first, yet, when thinking of methods this way, it is even simpler and makes sense. Think of:

func (p *Sometype) Somemethod (firstArg int) {} 

as having the literal meaning:

func SometypeSomemethod(p *Sometype, firstArg int) {}

and in this view, the body of SometypeSomemethod() is certainly free to test it's (actual) first argument (p *Sometype) for a value of nil.

like image 144
Mark Avatar answered Oct 13 '22 20:10

Mark


Please read: https://golang.org/ref/spec#The_zero_value

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

So in your code, var err *MyError is nil, you can call err.Error() is ok even err = nill,

**

In Go, the function to be called by the Expression.Name() syntax is entirely determined by the type of Expression and not by the particular run-time value of that expression, including nil.

**

like image 7
BlackMamba Avatar answered Oct 13 '22 19:10

BlackMamba