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)
}
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".
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).
nil
valueA 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 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
.
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.