GoGo example

How to check if a key exists in a map in Go

5 min read▶ Runs in an isolated hosted runtimeUpdated Jul 2026

Quick answer

Use the comma-ok idiom: value, ok := m[key]ok is true if the key is present. Just checking existence? Discard the value: _, ok := m[key]. There is no m.Contains(key); this two-value form is the API.

Indexing a Go map never fails: m[key] on a missing key quietly returns the value type's zero value0, "", nil — with no error and no panic. That's convenient right up until a stored zero and a missing key need to mean different things, which is why the map index has a second form that also returns a boolean. Each example below runs on this page — hit Run, then edit the code and run it again.

1The comma-ok idiomRecommended

Assigning a map index to two variables switches it into its second form: value, ok := m[key]. For a present key you get the stored value and true; for a missing one, the zero value and false. Most Go code folds the lookup into the if statement itself, which keeps both variables scoped to the branch that uses them.

exists.go

Output

Prints 443 true, 0 false, and ssh runs on port 22. The name ok is convention, not keyword — but it's a strong one; every Go reader recognizes v, ok := on sight, so don't get creative with it.

2The trap: missing key vs stored zero

Why not just compare against the zero value — if m[key] != 0? Because a map can legitimately store a zero. A product with 0 stock and a product you never stocked both index to 0; only the ok boolean can tell you which is which. This distinction is the entire reason the idiom exists.

zero.go

Output

The first two lines both print 0 — indistinguishable. With comma-ok, chairs comes back as 0 true (present, zero stock) and desks as 0 false (absent). Same value, opposite meanings — carried entirely by the boolean.

3Existence-only checks and sets

When only membership matters, discard the value with the blank identifier: _, ok := m[key]. Push that one step further and you get Go's idiomatic set: a map whose values carry no information at all. map[string]struct{} is the classic choice — the empty struct occupies zero bytes — while map[string]bool trades a byte per entry for a friendlier read at the call site.

set.go

Output

Prints alice already visited, carol in set: true, members: 3, then true false. The bool version's charm is that last line: admins["bob"] is a missing key, so it evaluates to false — exactly what "not an admin" should say, no comma-ok needed. It only misleads if you ever store an explicit false.

4Which form should you use?

FormTells youBest for
v, ok := m[k]Value + existenceYou need the value — almost everything
if v, ok := m[k]; ok { … }Value + existence, scopedThe idiomatic guard around use
_, ok := m[k]Existence onlyMembership tests, sets
v := m[k]Value only — zero if missingCounters and defaults, where zero is fine
if m[k] != zero { … }Existence, wronglyNothing — breaks on stored zeros

5Checking if a value exists

Comma-ok answers "is this key here?" in O(1). The reverse question — does any key map to this value? — has no shortcut: maps only index one way, so you range over the entries. If you ask it often, build the inverse map once and do comma-ok lookups on that instead.

values.go

Output

Prints https. One thing to know before you extend this: Go deliberately randomizes map iteration order on every run, so if several keys matched, which one wins the break would vary run to run — collect all matches (or sort the keys first) when that matters.

6nil maps, delete, and len

A declared-but-uninitialized map is nil, and it behaves asymmetrically: reading is completely safe — every key is simply missing, comma-ok included — but writing panics. That asymmetry makes lookup code robust by default and is why the panic assignment to entry in nil map always points at a missing make. Rounding out the toolkit: delete(m, k) removes a key and is a silent no-op if it wasn't there.

nilmap.go

Output

Prints 0 false 0 from the nil map — zero value, not present, length zero — and false 0 after the delete. Note that delete returns nothing: if you need to know whether the key was there, do a comma-ok check first.

Frequently asked questions

How do I check if a key exists in a Go map without using the value?

Discard the value with the blank identifier: _, ok := m[key]. ok is true if the key is present. This is the standard existence-only form — there is no m.Contains(key) or m.HasKey(key) method in Go.

What does a Go map return for a key that doesn’t exist?

The zero value of the map’s value type — 0 for numbers, "" for strings, nil for pointers, slices, and maps — with no error or panic. That’s why you can’t detect a missing key from the value alone when zeros are legitimately stored; the two-value form v, ok := m[key] exists precisely to tell those cases apart.

Why doesn’t Go have a maps.Contains or HasKey function?

Because the comma-ok index form already is the API: _, ok := m[key] compiles to a single map lookup and works for every key and value type. A maps.Contains helper has been proposed for the standard library, but the two-value index remains the idiomatic check that all Go code uses and every Go reader expects.

How do I check whether a value (not a key) exists in a map?

Loop over the map: for _, v := range m { if v == target { … } }. Maps only index by key, so a value search is O(n). If you do it repeatedly, invert the map once — build map[V]K (or map[V][]K when values repeat) — and then use the O(1) comma-ok lookup on the inverse.

Is it safe to check a key on a nil map?

Yes. Reading from a nil map is fully defined: m[key] returns the zero value, _, ok := m[key] returns false, len(m) is 0, and ranging over it does nothing. Only writing to a nil map panics — initialize with make (or a literal) before the first assignment.