216

I define a struct ...

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

Sometimes I assign an empty session to it (because nil is not possible)

session = Session{};

Then I want to check, if it is empty:

if session == Session{} {
     // do stuff...
}

Obviously this is not working. How do I write it?

3
  • 8
    Session{} isn't an "empty" session; it's initialized with each field being the zero value. Commented Feb 11, 2015 at 6:08
  • I am curious, why don't you use an interface instead? If I were to write a session handler, I would not expect a session with empty fields. Could lead to more problem than its worth.
    – magum
    Commented Sep 10, 2021 at 9:51
  • Maybe you want to formulate this as an answer. I cannot recall why I did this in this project in 2015 ;-)
    – Michael
    Commented Sep 11, 2021 at 15:35

9 Answers 9

334

You can use == to compare with a zero value composite literal because all fields in Session are comparable:

if (Session{}) == session  {
    fmt.Println("is zero value")
}

playground example

Because of a parsing ambiguity, parentheses are required around the composite literal in the if condition.

The use of == above applies to structs where all fields are comparable. If the struct contains a non-comparable field (slice, map or function), then the fields must be compared one by one to their zero values.

An alternative to comparing the entire value is to compare a field that must be set to a non-zero value in a valid session. For example, if the player id must be != "" in a valid session, use

if session.playerId == "" {
    fmt.Println("is zero value")
}
5
  • 11
    So I get an error, struct containing []byte cannot be compared, because, well, my struct contains a byte slice.
    – Nevermore
    Commented Aug 16, 2016 at 1:46
  • 28
    @Nevermore The answer applies to a struct with comparable fields. If your struct contains non-comparable values like []byte, then you need to write code to test all fields or use the reflect package as outlined in another answer. Commented Aug 16, 2016 at 3:10
  • 2
    As mentioned by @Nevermore, == comparison with slice fields will fail. For comparing these structs, use either reflect.DeepEqual or consider something more specialized like discussed here: stackoverflow.com/questions/24534072/…
    – asgaines
    Commented Oct 29, 2018 at 5:07
  • 1
    "parsing ambiguity in [if condition]" saved my day, thanks :) coz when I was trying it in fmt.Println(session == Session{}), it works.
    – Franva
    Commented Feb 19, 2019 at 23:58
  • 1
    In performance-sensitive scenarios, is checking a struct value faster than empty struct comparison? e.g session.timestamp == nil vs (Session{}) == session Commented Aug 1, 2019 at 13:53
67

Here are 3 more suggestions or techniques:

With an Additional Field

You can add an additional field to tell if the struct has been populated or it is empty. I intentionally named it ready and not empty because the zero value of a bool is false, so if you create a new struct like Session{} its ready field will be automatically false and it will tell you the truth: that the struct is not-yet ready (it's empty).

type Session struct {
    ready bool

    playerId string
    beehive string
    timestamp time.Time
}

When you initialize the struct, you have to set ready to true. Your isEmpty() method isn't needed anymore (although you can create one if you want to) because you can just test the ready field itself.

var s Session

if !s.ready {
    // do stuff (populate s)
}

Significance of this one additional bool field increases as the struct grows bigger or if it contains fields which are not comparable (e.g. slice, map and function values).

Using the Zero Value of an Existing Field

This is similar to the previous suggestion, but it uses the zero value of an existing field which is considered invalid when the struct is not empty. Usability of this is implementation dependant.

For example if in your example your playerId cannot be the empty string "", you can use it to test if your struct is empty like this:

var s Session

if s.playerId == "" {
    // do stuff (populate s, give proper value to playerId)
}

In this case it's worth incorporating this check into an isEmpty() method because this check is implementation dependant:

func (s Session) isEmpty() bool {
    return s.playerId == ""
}

And using it:

if s.isEmpty() {
    // do stuff (populate s, give proper value to playerId)
}

Use Pointer to your struct

The second suggestion is to use a Pointer to your struct: *Session. Pointers can have nil values, so you can test for it:

var s *Session

if s == nil {
    s = new(Session)
    // do stuff (populate s)
}
1
  • 2
    Awesome answer! I think following the last choice looks pretty idiomatic. Commented Oct 16, 2017 at 12:31
40

Just a quick addition, because I tackled the same issue today:

With Go 1.13 it is possible to use the new isZero() method:

if reflect.ValueOf(session).IsZero() {
     // do stuff...
}

I didn't test this regarding performance, but I guess that this should be faster, than comparing via reflect.DeepEqual().

0
30

Using reflect.deepEqual also works, especially when you have map inside the struct

package main

import "fmt"
import "time"
import "reflect"

type Session struct {
    playerId string
    beehive string
    timestamp time.Time
}

func (s Session) IsEmpty() bool {
  return reflect.DeepEqual(s,Session{})
}

func main() {
  x := Session{}
  if x.IsEmpty() {
    fmt.Print("is empty")
  } 
}
1
  • 3
    using reflect.DeepEqual is a very clean solution but i wonder if it takes more processing time? i assume it is comparing every field, plus you introduce a new import.
    – thurt
    Commented Dec 20, 2017 at 4:10
8

Keep in mind that with pointers to struct you'd have to dereference the variable and not compare it with a pointer to empty struct:

session := &Session{}
if (Session{}) == *session {
    fmt.Println("session is empty")
}

Check this playground.

Also here you can see that a struct holding a property which is a slice of pointers cannot be compared the same way...

2

As an alternative to the other answers, it's possible to do this with a syntax similar to the way you originally intended if you do it via a case statement rather than an if:

session := Session{}
switch {
case Session{} == session:
    fmt.Println("zero")
default:
    fmt.Println("not zero")
}

playground example

1

@cerise-limón's answer was the best. Though, if you get struct containing ... cannot be compared error like below.

type Friends struct {
    nice []string
    mute []string
}

type Session struct {
    playerId  string
    beehive   string
    timestamp time.Time
    friends   Friends
}

func main() {
    session := Session{}
    if (Session{}) == session  {
        fmt.Println("zero")
    }
}

// Output: ./prog.go:23:17: invalid operation: Session{} == session (struct containing Friends cannot be compared)

The above, Friends' object is causing an error.

The easy way to fix it is to define as a pointer in the field.

type Friends struct {
    nice []string
    mute []string
    
}

type Session struct {
    playerId  string
    beehive   string
    timestamp time.Time
    friends   *Friends // <--- here
}

func main() {
    session := Session{}
    if (Session{}) == session  {
        fmt.Println("zero")
    }
}

// Output: zero
0

Using fmt.Sprintf can help to detect is a struct empty or not. When it is empty the output is { }.

This suggestion is useful for working with JSONs and where the struct doesn't have a specific name.

if fmt.Sprintf("%v", session) == "{ }" {
    // session is empty
}
0

there are two ways

  1. using reflect.DeepEqual(str1, str2)
package main

import (
    "fmt"
    "reflect"
)

type Session struct {
    playerId string
    beehive  string
}

func main() {
    s1 := Session{
        playerId: "abc",
        beehive:  "hee",
    }
    s2 := Session{
        playerId: "abc",
        beehive:  "heef",
    }

    fmt.Println(reflect.DeepEqual(s1, s2)) //false
}

  1. empty check
if (Session{}) == session  {
    fmt.Println("is zero value")
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.