214

I see a lot of code in Go to detect nil, like this:

if err != nil { 
    // handle the error    
}

however, I have a struct like this:

type Config struct {
    host string  
    port float64
}

and config is an instance of Config, when I do:

if config == nil {
}

there is compile error, saying: cannot convert nil to type Config

John Weldon
  • 39,849
  • 11
  • 94
  • 127
  • 4
    I dont understand why port is of type float64 ? – alamin Nov 28 '18 at 02:11
  • 4
    It shouldn't be. Go's JSON api imports any number from JSON into float64, I have to convert the float64 to int. –  Nov 29 '18 at 09:10

6 Answers6

218

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
}
alamin
  • 2,377
  • 1
  • 26
  • 31
Oleiade
  • 6,156
  • 4
  • 30
  • 42
  • 7
    I guess `var config &Config // nil` should be: `var config *Config` – Tomasz Plonka Sep 08 '16 at 14:32
  • `var config *Config` crashes with `invalid memory address or nil pointer dereference`. Maybe we need `var config Config` – kachar Oct 19 '16 at 22:51
  • I understand the reasoning behind this choice may not be yours, but it makes *no* sense to me that "if !(config != nil)" is valid but that "if config == nil" is not. Both are doing a comparison between the same struct and non-struct. – retorquere Jan 16 '17 at 18:22
  • @retorquere they are both invalid, see https://play.golang.org/p/k2EmRcels6. Whether it's '!=' or '==' makes no difference; what does make a difference is whether config is a struct or pointer to struct. – stewbasic Oct 18 '17 at 02:28
  • @Madeo I do not see a contradiction between your example and this answer. The fact that the structure you use has no elements has no effect on the fact you have properly initialized a pointer with address of a struct literal, and therefore it is not nill – Petr Jun 24 '21 at 14:24
69

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 }
nemo
  • 55,207
  • 13
  • 135
  • 135
  • 5
    Further to the above, that is why `time.Time` has an `IsZero()` method. However you could also do [`var t1 time.Time; if t1 == time.Time{}`](https://play.golang.org/p/n7EfFVlzrB) and you *could* also do `if config == Config{}` to check all the field for you (struct equality is well defined in Go). However, that's not efficient if you have lots of fields. And, perhaps the zero value is a sane and useable value so passing one in isn't special. – Dave C Apr 06 '15 at 03:10
  • 1
    The Initialized function will fail, if Config as a pointer is accessed. It could be changed to `func (c *Config) Initialized() bool { return !(c == nil) }` – Sundar Mar 06 '17 at 09:45
  • @Sundar in this case it might be convenient to do it this way, so I applied the change. However, normally I would not expect the receiving end of the method call to check if itself is nil, since this should be the job of the caller. – nemo Mar 06 '17 at 15:21
22

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}
Taavi
  • 135
  • 2
  • 16
9

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.

mrpandey
  • 627
  • 2
  • 9
  • 17
7

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

Thellimist
  • 3,757
  • 5
  • 31
  • 49
3

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.
supei
  • 55
  • 6