在struct元素上调用一个函数,该函数是一个nil指针

In Go, however, 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 - copied

So we can call a method using a struct instance which is nil.

Consider the following program:

 package main

    import "fmt"

    type T struct {
        V int
        tt *T
    }

    func (t *T) hello() string {
       return "world"
   } 

    func main() {
        var t *T = nil
        fmt.Println(t, t.hello()) // <nil> world
        fmt.Println(t, t.tt.hello()) // panic
    }

Why fmt.Println(t, t.hello()) worked?

But

fmt.Println(t, t.tt.hello()) panicked?.

My understanding is that both t and t.tt are nil pointers. So t.tt.hello() should not panic as Calling a method on a nil struct pointer is allowed in golang.

My understanding is that both t and t.tt are nil pointers. So t.tt.hello() should not panic as Calling a method on a nil struct pointer is allowed in golang.

Your "understanding" is wrong.t.tt should and does panic.


Go 1.2 Release Notes (December 2013)

Use of nil

The language now specifies that, for safety reasons, certain uses of nil pointers are guaranteed to trigger a run-time panic. For instance, in Go 1.0, given code like

type T struct {
    X [1<<24]byte
    Field int32
}

func main() {
    var x *T
    ...
}

the nil pointer x could be used to access memory incorrectly: the expression x.Field could access memory at address 1<<24. To prevent such unsafe behavior, in Go 1.2 the compilers now guarantee that any indirection through a nil pointer, such as illustrated here but also in nil pointers to arrays, nil interface values, nil slices, and so on, will either panic or return a correct, safe non-nil value. In short, any expression that explicitly or implicitly requires evaluation of a nil address is an error. The implementation may inject extra tests into the compiled program to enforce this behavior.

Further details are in the design document.


In short, any expression that explicitly or implicitly requires evaluation of a nil address is an error.

Therefore, the following behavior is expected. The indirection for t.tt through a nil value of t fails with a panic.

package main

import "fmt"

type T struct {
    V  int
    tt *T
}

func (t *T) hello() string {
    return "world"
}

type A struct {
    a int
}

func main() {
    var t *T = nil
    fmt.Println(t)            // nil
    fmt.Println(t.tt.hello()) // panic
}

Playground: https://play.golang.org/p/Szwx5MqNHkQ

Output:

<nil>
panic: runtime error: invalid memory address or nil pointer dereference
main.main()
    /tmp/sandbox136049644/main.go:21 +0x84

nil is the zero value for pointers

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

Source: https://golang.org/src/builtin/builtin.go?h=nil#L101

To understand nil this video is fantastic
https://www.youtube.com/watch?v=ynoY2xz-F8s

Precisely speaking, t is a nil pointer, and t.tt doesn't exist at all. The panic you see is the result of dereferencing t, not t.tt. This is just obscured by the fact that t.tt is (or would be, if t were initialized) also a pointer.

This can be made more clear by accessing the V field of t:

func (t *T) foo() {
    fmt.Println(t.V) // Will panic, if `t` is nil
}

The reason the first test doesn't panic is that calling a method on t doesn't actually dereference t. Calling t.Hello() is roughly the equivalent of calling Hello(t), so won't panic unless/until t is actually dereferenced within the function.

t is nil,there is no t.tt.

And t.hello() is like hello(t), hello(nil) don't panic, but t.tt do.

Remember: a method is just a function with a receiver argument.

Method expressions