How to check if a key exists in a map in Go
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 value — 0, "", 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.
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.
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.
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?
| Form | Tells you | Best for |
|---|---|---|
| v, ok := m[k] | Value + existence | You need the value — almost everything |
| if v, ok := m[k]; ok { … } | Value + existence, scoped | The idiomatic guard around use |
| _, ok := m[k] | Existence only | Membership tests, sets |
| v := m[k] | Value only — zero if missing | Counters and defaults, where zero is fine |
| if m[k] != zero { … } | Existence, wrongly | Nothing — 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.
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.
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.