Lesson 2 · Foundations

Types & zero values

There is no uninitialised memory in Go — and that's a design tool.

Your win: declare variables the idiomatic way, and explain what "the zero value" is and why Go programs (and our repo) lean on it to avoid constructors and null checks.

Declarations, quickly

var count int          // explicit type, zero value (0)
name := "spike"          // short form, type inferred — only inside functions
const maxRetries = 10    // compile-time constant
var ids []string        // a nil slice — still usable

Two rules that surprise newcomers: types are static and strict — there are no implicit numeric conversions, so you must write float64(count) explicitly; and := only works inside a function (package-level needs var).

Every type has a zero value

Declare a variable without initialising it and Go gives it a well-defined zero value — never random memory.1

TypeZero value
numbers (int, float64)0
string"" (empty, not nil)
boolfalse
pointer, slice, map, channel, func, interface, errornil
structevery field set to its own zero value

"Make the zero value useful" — a proverb with teeth

Go's standard library is designed so the zeroed value already works, so callers don't need a constructor.2 A var mu sync.Mutex is ready to Lock(). A nil slice can be append-ed and range-d. A bytes.Buffer works from zero.

Anchor — your stateless adapters Our repositories are type EmailRepo struct{} — an empty struct (spike/.../infrastructure/repositories/email.go:17). Its zero value is a fully working adapter, so the composition root just writes &repositories.EmailRepo{} with nothing to configure (email_modifier_service.go:39). And type Emails []*Email starts life nil yet Add() appends to it fine — the zero value at work.
The catch (a Lesson-4 preview) A nil map is the exception: you can read it (you get zero values back), but writing to a nil map panics. Maps must be make-d before writing. Slices don't have this problem.

Why this matters for interviews

The zero value is the reason Go rarely needs null checks or "is it initialised?" ceremony, and why constructors are optional rather than mandatory. Being able to say that — and name the nil-map exception — signals you actually think in Go, not just write it.

Do this next

A Tour of Go — "Basics" (variables, zero values, type conversions)

Interactive; type a wrong conversion and watch it fail to compile. Then skim Effective Go's short section on zero values.

go.dev/tour — zero values
Effective Go — allocation & zero values

Check yourself (from memory)

Q1. An uninitialised Go variable holds…

Go zeroes memory: 0, "", false, or nil depending on type. Never garbage.

Q2. Why can &repositories.EmailRepo{} be used with no configuration?

It's a stateless struct{} whose zero value already works — "make the zero value useful" applied to our repositories.

Q3. Assigning an int into a float64 variable needs…

Go has no implicit numeric conversion — float64(n). Strict static typing, caught at compile time.
What does "make the zero value useful" mean — with a repo example?
recall, then click to reveal
Design types so the zeroed-out value works with no init, sparing callers a constructor. In the stdlib: sync.Mutex, bytes.Buffer, a nil slice (append/range). In our repo: type EmailRepo struct{} is a working adapter straight from its zero value, and type Emails []*Email is nil-then-appendable.
Curious about iota, typed vs untyped constants, or why string zero is "" not nil? Ask me.

1. Go spec — the zero value.

2. Go Proverbs — "Make the zero value useful"; Effective Go.