如何在Golang中检查指向结构net.IPNet的Pointer的nil值

How can I check the nil value as a result of the assignment of variable b below?

package main

import (
    "fmt"
    "net"
)


type Subnet struct {
    ID                int
    IP                *net.IPNet
}

func main() {
    var s = Subnet{
        ID: 12345,
        IP: &net.IPNet{
            IP:   net.IP{10, 1, 232, 0},
            Mask: net.IPMask{255, 255, 255, 0},
        },
    }
    fmt.Printf("%+v
", s)
    var b = Subnet{
        ID: 12345,
        IP: &net.IPNet{},
    }
    fmt.Printf("%+v
", b)
    if b.IP == nil {
        fmt.Println("hello there")
    }
}

Here is the go playground https://play.golang.org/p/Jk6_3ofyH5

Basically, I expect that "hello there" will be printed out as b.IP is nil, but it did not.

The check should be like, if b.IP != nil { fmt.Println("hello there") } And the output will have hello-world,

{ID:12345 IP:10.1.232.0/24} {ID:12345 IP:} hello there

Note:Golang's nil is not same as Cpp's NULL, it is the opposite.

nil is a predeclared identifier in Go. For example,

package main

import (
    "fmt"
    "net"
)

type Subnet struct {
    ID int
    IP *net.IPNet
}

func main() {
    var b = Subnet{ID: 1, IP: nil}
    fmt.Println(b.IP == nil)
}

Output:

true

Playground: https://play.golang.org/p/-745-hhLGg

Notice that IPNet has String() string function which makes it implements Stringer interface. Basically this function provides custom string representation of the struct when the struct is printed. If you dig in further to the source code you'll see that this function returns string "<nil>" when either IPNet.IP or IPNet.Mask is nil. This explained how your code prints <nil> when the value of Subnet.IP field is actually not nil.

Now to answer the question in the title literally, you already did the check correctly by comparing the value with nil. In addition, you can try to print using %#v instead, for example :

func main() {
    var b = Subnet{ID: 12345, IP: &net.IPNet{}}
    fmt.Printf("%#v
", b)
    fmt.Printf("is null: %t
", b.IP == nil)
    var c = Subnet{ID: 12345, IP: nil}
    fmt.Printf("%#v
", c)
    fmt.Printf("is null: %t", c.IP == nil)
}

playground

output :

main.Subnet{ID:12345, IP:(*net.IPNet)(0x10444340)}
is null: false
main.Subnet{ID:12345, IP:(*net.IPNet)(nil)}
is null: true

It looks like you're confusing the value nil with a concept called "zero value".

The zero value

Any type in Go — no matter whether standard or created by the user — has a "zero value" which is the value assigned automatically to any variable of that type which was not explicitly initialized otherwise. IOW, when you have

type T …

var v T

the value of the variable "v" will be the zero value of type T.

The concept of the zero value follows naturally from the Go's property of not allowing uninitialized variables: variables in Go always have sensible values (contraty to say, C).

The nil value

A special value, nil, which is a predeclared identifier in Go, is the zero value for a set of types which have reference semantics.

The term "reference semantics" might be intimidating but it can be explained pretty simply: a type has reference semantics when variables of it reference some data structure instead of directly containing it. Hence when you copy the value of a variable of such type into another variable, both variables reference the the same data structure.

As you should know, maps, slices, interfaces and pointers have reference semantics in Go, and that's why their zero values are nil.

The distinction

The major takeaway to draw from the above is that nil is the zero value some types which have a certain property, but not for all of them.

For example, for

type T struct {
  A int
  B string
}

the zero value is

T{
  A: 0,
  B: "",
}

that is, a value of type T having its fields initialized to the zero values corresponding to the types of those fields: 0 for int and "" for string.

Your particular problem

In your type

type Subnet struct {
    ID                int
    IP                *net.IPNet
}

the field "IP" has a pointer type *net.IPNet, which is a pointer to a value of the type net.IPNet. Hence its zero value is nil.

Now if you were to declare something like

var subnet Subnet

this variable would start its life initialized to the zero value of its type, and that would means that its field "IP" would have the zero value for its type, nil.

In your example, you do:

var b = Subnet{
    ID: 12345,
    IP: &net.IPNet{},
}

and that means creating a variable "b" initialized to a particular value defined by the literal placed on the right hand of the assignment operator =.

The field "IP" in that literal is not initialized to the zero value of its type; instead, it's initialized to a pointer containing the address of an anonymous variable wihch has the type net.IPNet and containing the zero value of that type.

To say this in different words, the result it roughly equivalent to the following:

var ip net.IPNet

var b = Subnet{
    ID: 12345,
    IP: &ip,
}

There, the variable "ip" contains the zero value of its type, and the variable "b.IP" contains a pointer to the variable "ip".

Since that field "IP" points to some real memory location, its value is obviosly not nil, which makes a value of a type having refence semantics "point to nowhere", and that's why the test in your if statement fails.