Currently available · INDIA
← Writing
Languages·Aug 9, 2026·4 min read

Why Go Doesn't Let Variables Stay Uninitialized

Variables, types, constants, zero values, and := — explained simply. Why every Go variable starts with a real value, and how that stops a whole class of bugs.

0

Why Go Doesn't Let Variables Stay Uninitialized

In many languages, a new variable can be empty in a dangerous way: “I exist, but nobody put anything in me yet.” Read that variable and you might get random junk from memory.

Go says no to that.

Every variable starts with a real value. Either you set it — or Go sets a safe default called the zero value.

Forget “what is int?” for a second. The sticky question is:

Why does every variable already have a value — and how does that stop bugs?

Also read: Why Simple Code Beats Clever Code and Why Google Invented Go.

Have you been bitten by an uninitialized variable in another language?

---

Remember this one line

If a variable exists in Go, it already has a value.

No ghosts. No “maybe garbage.” No surprise crashes from unread memory.

var, types, const, and := all live under that rule.

---

Variables, types, constants, zeros, and :=

1 of 5

Variables

A variable is a labeled box. In Go, the box is never empty when you create it — it always starts with something real.

How you write it. Write var count int, or var name = "Ada". If you give a value, Go can often guess the type for you.

Why it sticks. In C, a new local can hold leftover junk from memory. In Go, that never happens. Forget to set a number? You still get 0 — not a random crash later.

Don’t get tripped. A variable at package level lives for the whole program. Keep most variables short-lived inside functions. Shared globals are easy to regret.

Example

var count int
var name string = "Ada"
var ready = true

---

What each zero value really means

int starts at 0. Easy. The harder part is knowing which zeros are safe to use — and which ones blow up.

By type

What Go puts in the box first — and whether that starter is safe to use right away.

int / float64

0 / 0.0

Picture it. You start at nothing to add. Perfect for counters and totals.

Safe to use. Just write var sum int and start adding. You already begin at 0.

Watch out. Sometimes 0 means “none yet,” sometimes it means “really zero.” If both matter, don’t guess — use a flag or a pointer.

---

var, :=, and const

Same job, different tools.

Compare

var — clear and steady

var total int
total = sum(items)

var name string = "Ada"

:= — quick and local

total := sum(items)
name := "Ada"

Have a value ready inside a function? Use :=. Want the zero first, or a package-level name? Use var.

---

Why this design choice exists

Two questions

The design choice underneath the syntax.

Why does every variable already have a value?

Because “I forgot to set this” is a classic bug — and Go won’t leave that trap in the language.

Why it helps. In older languages like C, a new variable can hold random leftover bits from memory. Sometimes it works. Sometimes it explodes. Those bugs are nightmare fuel. Go’s fix is simple: create a variable, get a known starter value. Always.

But wait. A known starter can still be the wrong answer for your problem. If 0 means both “unset” and “zero retries,” you’ve made a new mess — just a quieter one.

Zeros kill random garbage. They don’t kill fuzzy meaning. Use the zero when empty is a great start. When “empty” and “missing” differ, say missing clearly — with ok, a pointer, or a small status type.

---

In real code

Counting is free

go
var sum int
for _, n := range values {
    sum += n
}

You don’t write sum = 0. Zero is already the right place to start adding.

Sticky idea: Go’s default often matches the math you wanted.

Zero is not the same as “missing”

go
func port(cfg map[string]int) int {
    if p, ok := cfg["port"]; ok {
        return p
    }
    return 8080
}

Is 0 a real port, or did someone forget to set one? If both are possible, don’t make 0 mean “I forgot.” Ask the map: “was this key there?”

Sticky idea: Empty ≠ missing. Say missing out loud in code.

Good types work on day zero

go
var mu sync.Mutex
mu.Lock()
defer mu.Unlock()

No NewMutex(). It just works. When you invent a type, ask: can someone write var t T and use it safely?

Sticky idea: The best Go types are ready before you “set them up.”

:= is fast — and sneaky

go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()

b, err := io.ReadAll(f) // same err, new b — allowed
if err != nil {
    return err
}

:= creates what’s new and reuses what’s old. Handy. Also easy to accidentally create a second err inside an if and wonder why the outer one never updates.

Sticky idea: Always glance at the left side of :=.

---

Try this

  1. Find a var with no =. Say its starting value out loud. Is that a good start?
  2. Find a place where 0 or "" means “not set.” Fix it so “missing” is clear.
  3. Build a tiny type that works with var t T — no constructor needed.
  4. Search for := inside if. Check you didn’t hide err by accident.
  5. Print a nil slice and an empty slice as JSON. Notice null vs [].

---

Take these home

  1. No empty ghosts — every variable starts with a value.
  2. Types are rules — they tell the compiler what you’re allowed to do.
  3. const is frozen at build time — not a variable you promise not to change.
  4. := for quick locals · var when you want the zero (or package scope).
  5. Zero stops random bugs — it does not stop “I used 0 to mean missing.”
  6. Design for the default — make var t T safe when you can.

---

Picture a blank form that somehow still has random scribbles in the boxes. That’s uninitialized memory.

Go hands you a clean form every time — zeros filled in. Your job is simpler: write the real answers, and when “blank” and “missing” are different things, say so in the code.

/ Stay in the loop

Get the next one in
your inbox.

New essays and field notes sent only when there's something worth sending. No tracking, no spam, easy unsubscribe.

Join · No spam · One-click unsubscribe