Question
Given this Go struct:
type Session struct {
playerId string
beehive string
timestamp time.Time
}
Sometimes an empty value is assigned because a struct value cannot be nil:
session = Session{}
How can you check whether session is empty? Why does the following condition not compile?
if session == Session{} {
// Do something.
}
Short Answer
You will learn that Go structs always have a zero value, how to compare a comparable struct with its zero-value literal, and when an explicit IsEmpty method is clearer than comparing an entire struct.
Concept
A Go struct value cannot be nil because it is a value, not a pointer. Every struct type has a zero value: a struct in which each field has that field type's zero value.
For Session, the zero value is equivalent to:
Session{
playerId: "",
beehive: "",
timestamp: time.Time{},
}
You can compare a struct to its zero value only when every field in the struct is comparable. In this example:
stringvalues are comparable.time.Timeis comparable.
Therefore Session is comparable.
The original condition fails because Go can interpret the {} after Session as the beginning of the if statement's block. Put parentheses around the composite literal to make the expression unambiguous:
if session == (Session{}) {
// The session has the struct's zero value.
}
This checks for the zero value, not necessarily a business-level definition of “empty.” For example, an application might consider a session valid only when playerId is present, regardless of the timestamp.
Mental Model
Think of a struct as a form with several fields.
A zero-value struct is a brand-new blank form:
- text fields contain
"" - numeric fields contain
0 - booleans contain
false - a
time.Timefield contains its zero time
Comparing session with Session{} asks: “Is every box on this form still in its default blank state?”
If your program's idea of an empty form is different—for example, only the player ID matters—check that rule directly instead of checking every box.
Syntax and Examples
Use parentheses when comparing with a struct literal inside an if condition:
if session == (Session{}) {
// session is the zero value
}
Complete example:
package main
import (
"fmt"
"time"
)
type Session struct {
playerId string
beehive string
timestamp time.Time
}
func main() {
var session Session // Automatically receives its zero value.
if session == (Session{}) {
fmt.Println("No session data has been set")
}
session.playerId = "player-42"
if session != (Session{}) {
fmt.Println("The session contains at least one non-zero field")
}
}
var session Session and session = Session{} both produce the same zero-value Session.
If “empty” specifically means that the timestamp has not been assigned, use the API provided by time.Time:
if session.timestamp.IsZero() {
fmt.Println("No timestamp has been recorded")
}
Step by Step Execution
Consider this program:
var session Session
fmt.Println(session == (Session{}))
session.playerId = "player-42"
fmt.Println(session == (Session{}))
Execution trace:
var session Sessioncreates aSessionwith its zero values:playerIdis""beehiveis""timestampistime.Time{}
session == (Session{})compares each corresponding field with a zero-valueSession.- All fields match, so the first output is
true. session.playerId = "player-42"changes one field.- The next struct comparison finds that
"player-42" != "". - The second output is
false.
The output is:
true
false
Real World Use Cases
Checking a zero-value struct is useful when the zero value genuinely means “not set yet.” Common examples include:
- Optional cached data: determine whether a cache entry has been populated.
- Configuration values: identify an entirely default configuration object.
- State machines: determine whether a state record has not been initialized.
- Database or API results: detect a value returned without meaningful fields, when that is part of the API contract.
- Time fields: use
t.IsZero()to detect a missing creation, expiry, or completion time.
In many applications, checking one required field is more useful than comparing the whole struct:
if session.playerId == "" {
// A session cannot be used without a player ID.
}
Real Codebase Usage
In production Go code, developers often make the meaning of “empty” explicit.
Use a domain-specific method
type Session struct {
playerId string
beehive string
timestamp time.Time
}
func (s Session) IsEmpty() bool {
return s.playerId == "" &&
s.beehive == "" &&
s.timestamp.IsZero()
}
This is useful when callers should not need to know the fields that define emptiness.
Validate required values with guard clauses
Often, a session is not “empty” but still invalid. Validate the fields that matter:
func saveSession(session Session) error {
if session.playerId == "" {
return errors.New("player ID is required")
}
if session.timestamp.IsZero() {
return errors.New("timestamp is required")
}
// Save the session.
return nil
}
Use a pointer when absence is different from a zero value
If the important question is “does a session object exist at all?”, represent optionality with a pointer:
var session *Session
if session == nil {
// No session was supplied.
}
A pointer lets you distinguish nil (absent) from &Session{} (present but zero-valued).
Common Mistakes
Omitting parentheses around the struct literal
This does not compile in an if condition:
if session == Session{} {
}
Use parentheses:
if session == (Session{}) {
}
Assuming all structs are comparable
A struct cannot be compared with == if any field is not comparable. Slices, maps, and functions are not comparable except when compared to nil.
type Session struct {
tags []string
}
// Does not compile: []string is not comparable.
// if session == (Session{}) {}
Check fields explicitly instead:
if len(session.tags) == 0 {
// No tags.
}
Treating zero value and invalid value as the same thing
This check only says every field has its zero value:
session == (Session{})
A session with a player ID but no timestamp is not zero-valued, yet it may still be invalid. Write validation for the actual business rules.
Comparing times with == for general time equality
time.Time is comparable, so it can be part of a comparable struct. However, for determining whether a time is unset, prefer:
if session.timestamp.IsZero() {
}
For checking whether two meaningful times represent the same instant, prefer t1.Equal(t2) rather than t1 == t2.
Comparisons
| Need | Recommended approach | Why |
|---|---|---|
| Is every comparable field at its zero value? | session == (Session{}) | Concise whole-struct comparison. |
| Is one field missing? | session.playerId == "" or session.timestamp.IsZero() | Expresses the exact rule. |
| Does the value meet application requirements? | Validate() or explicit guard clauses | Separates validity from zero-value status. |
| Is the entire object absent? | *Session and session == nil | A value struct itself cannot be nil. |
| Does the struct contain slices or maps? | Check fields such as len(s.tags) == 0 | Such structs cannot use == as a whole. |
Session{} and var session Session both create the same zero-value state. Choose var for a declaration and Session{} when you want to construct or reset a value explicitly.
Cheat Sheet
// Declare a zero-value struct.
var session Session
// Explicitly reset to the zero value.
session = Session{}
// Compare a comparable struct to its zero value.
if session == (Session{}) {
// All fields are zero values.
}
// Check a required string field.
if session.playerId == "" {
// Missing player ID.
}
// Check whether a time is unset.
if session.timestamp.IsZero() {
// Missing timestamp.
}
// Represent an optional struct with a pointer.
var sessionPtr *Session
if sessionPtr == nil {
// No Session exists.
}
Rules:
- Struct equality works only if all fields are comparable.
string, numbers, booleans, pointers, andtime.Timeare comparable.- Slices, maps, and functions prevent whole-struct
==comparisons. - Parenthesize a composite literal in an
ifcomparison:(Session{}). - Prefer explicit validation when “empty” has business-specific meaning.
FAQ
Why can I not assign nil to a Go struct?
A struct variable holds a value directly. Only types such as pointers, maps, slices, channels, functions, and interfaces can have a nil value.
Is Session{} the same as var session Session?
Yes. Both produce a Session whose fields have their zero values.
Why are parentheses needed in session == (Session{})?
Without parentheses, the parser can treat {} as the start of the if body. Parentheses clearly mark Session{} as part of the comparison expression.
Can every Go struct be compared with ==?
No. Every field must be comparable. A struct containing a slice, map, or function cannot be compared as a whole with ==.
Should I use time.Time{} or IsZero()?
For an unset timestamp, use IsZero(). It clearly communicates your intent.
When should I use *Session instead of Session?
Use *Session when you need to distinguish an absent session (nil) from a present session whose fields happen to have zero values.
Is a non-zero struct always valid?
No. A struct can have one populated field and still be missing required data. Validate the fields your application requires.
Mini Project
Description
Build a small session validation helper for a game service. It demonstrates the difference between a completely zero-value struct, a partially filled struct, and a session that is ready to save.
Goal
Create methods that identify zero-value sessions and validate the fields required for a usable session.
Requirements
Requirement 1
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.