How to check for an empty struct?
StructGoStruct Problem Overview
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?
Struct Solutions
Solution 1 - Struct
You can use == to compare with a zero value composite literal because all fields are comparable:
if (Session{}) == session {
fmt.Println("is zero value")
}
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")
}
Solution 2 - Struct
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)
}
Solution 3 - Struct
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")
}
}
Solution 4 - Struct
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()
.
Solution 5 - Struct
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...
Solution 6 - Struct
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]
[1]: https://play.golang.org/p/kgIqmRnA2M "playground example"
Solution 7 - Struct
@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
Solution 8 - Struct
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
}