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 same data structure.
As you should know, maps, slices, interfaces, function values, 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
for 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 (or, we may say, an address of) 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 mean 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.
b.IPisn't nil.net.IPNetthree lines above the check:IP: &net.IPNet{}