去指针优先分配

While I was fooling around with closures, I stumbled upon this behavior. I can't wrap my head around it.

This code snippet works:

func main(){
    var a string = "foo"
    var b *string
    var c string = "bar"
    b = &c

    fmt.Printf("%s %s
", a, *b)

    a = "hello"
    *b = "world"

    fmt.Printf("%s %s
", a, *b)
}

Whereas this does not:

func main(){
    var a string = "foo"
    var b *string
    *b = "bar"

    fmt.Printf("%s %s
", a, *b)

    a = "hello"
    *b = "world"

    fmt.Printf("%s %s
", a, *b)
}

https://play.golang.org/p/NHw3X__Wtd

Could someone much cleverer than me please explain.

Each variable is initialized to the zero value of its type if you don't explicitly specify an initial value. Zero value for all pointer types is nil which means it does not point to anything (yet).

You may assign an address (pointer value) at any time to a variable of pointer type. But until you initialize your pointer variable (to anything other than nil), it points to nothing and thus you can't assign anything to where it points to (because that is "nothing").

In order to set the pointed value, initialize it first so it will actually point to somewhere which you can set/change. This initialization may be the address of some other variable (of the same type as the pointed type - called the element type), or the address of some newly allocated zero value of the pointed type returned by the builtin new() function.

This line

var b *string

just creates a new variable b of type *string, but it doesn't specify any initial value, so b will be initialized to nil. It does not point to anything (or to anywhere). If you attempt to set the pointed value:

var b *string
*b = "bar"

You get a runtime panic:

panic: runtime error: invalid memory address or nil pointer dereference

For you to be able to set the pointed value, you need to initialize it with a non-nil pointer value, e.g.:

b = new(string)

After this you can now set the pointed value:

*b = "world"

Note that when you do:

var b *string
var c string = "bar"
b = &c

This creates a variable b of type *string which will get the zero value nil. And creates a variable c of type string, and initializes it with the value "bar". And after that takes the address of c and stores it in the pointer variable b. Now after this if you modify the value pointed by b, that will "also" modify the value of the variable c because b points to c (or more precisely b stores the memory address where c is). So if after this you do:

*b = "world"

And you print the value of both c and *b, both will be "world":

fmt.Println(c, *b) // Prints "world world"