In the Go programming language, nil is a zero value. Recall from unit 2 that an integer declared without a value will default to 0. An empty string is the zero value for strings, and so on. A pointer with nowhere to point has the value nil .
Under the hood, an interface in Golang consists of two elements: type and value. When we assign a nil integer pointer to an interface in our example above, the interface becomes (*int)(nil) , and it is not nil. An interface equals nil only if both the type and value are nil.
nil is a frequently used and important predeclared identifier in Go. It is the literal representation of zero values of many kinds of types. Many new Go programmers with experiences of some other popular languages may view nil as the counterpart of null (or NULL ) in other languages.
The compiler is pointing the error to you, you're comparing a structure instance and nil. They're not of the same type so it considers it as an invalid comparison and yells at you.
What you want to do here is to compare a pointer to your config instance to nil, which is a valid comparison. To do that you can either use the golang new builtin, or initialize a pointer to it:
config := new(Config) // not nil
or
config := &Config{
host: "myhost.com",
port: 22,
} // not nil
or
var config *Config // nil
Then you'll be able to check if
if config == nil {
// then
}
In addition to Oleiade, see the spec on zero values:
When memory is allocated to store a value, either through a declaration or a call of make or new, and no explicit initialization is provided, the memory is given a default initialization. Each element of such a value is set to the zero value for its type: false for booleans, 0 for integers, 0.0 for floats, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.
As you can see, nil
is not the zero value for every type but only for pointers, functions, interfaces, slices, channels and maps. This is the reason why config == nil
is an error and
&config == nil
is not.
To check whether your struct is uninitialized you'd have to check every member for its
respective zero value (e.g. host == ""
, port == 0
, etc.) or have a private field which
is set by an internal initialization method. Example:
type Config struct {
Host string
Port float64
setup bool
}
func NewConfig(host string, port float64) *Config {
return &Config{host, port, true}
}
func (c *Config) Initialized() bool { return c != nil && c.setup }
I have created some sample code which creates new variables using a variety of ways that I can think of. It looks like the first 3 ways create values, and the last two create references.
package main
import "fmt"
type Config struct {
host string
port float64
}
func main() {
//value
var c1 Config
c2 := Config{}
c3 := *new(Config)
//reference
c4 := &Config{}
c5 := new(Config)
fmt.Println(&c1 == nil)
fmt.Println(&c2 == nil)
fmt.Println(&c3 == nil)
fmt.Println(c4 == nil)
fmt.Println(c5 == nil)
fmt.Println(c1, c2, c3, c4, c5)
}
which outputs:
false
false
false
false
false
{ 0} { 0} { 0} &{ 0} &{ 0}
You can also check like struct_var == (struct{})
. This does not allow you to compare to nil but it does check if it is initialized or not. Be careful while using this method. If your struct can have zero values for all of its fields you won't have great time.
package main
import "fmt"
type A struct {
Name string
}
func main() {
a := A{"Hello"}
var b A
if a == (A{}) {
fmt.Println("A is empty") // Does not print
}
if b == (A{}) {
fmt.Println("B is empty") // Prints
}
}
http://play.golang.org/p/RXcE06chxE
In Go 1.13 and later, you can use Value.IsZero
method offered in reflect
package.
if reflect.ValueOf(v).IsZero() {
// v is zero, do something
}
Apart from basic types, it also works for Array, Chan, Func, Interface, Map, Ptr, Slice, UnsafePointer, and Struct. See this for reference.
The language spec mentions comparison operators' behaviors:
comparison operators
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
Assignability
A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:
- x's type is identical to T.
- x's type V and T have identical underlying types and at least one of V or T is not a named type.
- T is an interface type and x implements T.
- x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a named type.
- x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
- x is an untyped constant representable by a value of type T.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With