In Go, how do you distinguish a missing map key from one whose stored value is zero?
answer
- a plain lookup never fails
- ask the index expression for a second result
- the second result is a bool, not an error
- same shape as a type assertion or channel receive
basics
~20 sUse the two-value comma-ok form: v, ok := m[key]. ok is true only when the key is present. A one-value lookup returns the value type's zero value for a missing key, so 0, an empty string or false cannot tell you which happened.
solid answer
~40 sA Go map lookup never fails: `m[key]` returns the value type's zero value when the key is absent, so for `map[string]int` you cannot tell a stored `0` from a missing key by the value alone. The two-value form `v, ok := m[key]` gives you the value plus a `bool` that is true only if the key is present, and it is the only correct presence test. It works in an `if` with an initialiser, `if v, ok := m[k]; ok { ... }`, which also scopes `v` to the branch. If you only care about presence, discard the value: `if _, ok := set[k]; ok`. Reading a missing key is a pure read — unlike some languages' default-dict behaviour it does not insert anything, so `len(m)` is unchanged afterwards.
code
go · 9 linescounts := map[string]int{"go": 0}
if v, ok := counts["go"]; ok {
fmt.Println("stored:", v) // prints: stored: 0
}
if _, ok := counts["rust"]; !ok {
fmt.Println("absent") // prints: absent
}
fmt.Println(len(counts)) // prints: 1 — the failed read inserted nothinggo deeper
Be ready to write v, ok := m[key] from memory and say what each result holds when the key is missing. Know that a plain lookup returns the zero value and never panics.
Explain that comma-ok is a language form, not a method, and that it shares its shape with type assertions and channel receives. Say why a read cannot insert and when the zero-value default is preferable to checking ok.
Show judgment about when absence and zero genuinely differ in a real API — an empty result set versus a term never indexed — and design the map's value type so callers are not forced to guess.
Frame it as an API question: a map handed across a package boundary makes absence and zero indistinguishable to every caller, so decide early whether the exported shape should be a map, an accessor returning (value, bool), or a type whose zero value is meaningful.
## The lookup that cannot fail In Go, indexing a map is a total operation. For a map `m` of type `map[K]V`, the expression `m[k]` always produces a value of type `V`. If `k` is present, you get the stored value; if it is absent, you get **the zero value of `V`** — `0` for numeric types, `""` for `string`, `false` for `bool`, `nil` for pointer, slice, map, channel, function and interface types, and a struct with every field zeroed for a struct type. That design removes a whole class of errors (there is no `KeyError`, no exception, no need to guard every read), but it creates one ambiguity: **absence and a stored zero look identical.** A word-frequency map of type `map[string]int` that legitimately stores `0` for a term you have seen but not yet counted is indistinguishable, by value, from a term the map has never heard of. ## The comma-ok form Go resolves this with a second, two-valued form of the index expression: ```go v, ok := m[k] ``` Here `v` has type `V` as before, and `ok` is an untyped boolean that is `true` exactly when `k` was present. This is a special form of the language, not a function call and not a method — it is only valid in an assignment (or short variable declaration) with two operands on the left. The same comma-ok shape appears in two other places in Go: a type assertion (`s, ok := x.(string)`) and a channel receive (`v, ok := <-ch`), so recognising it is worth more than one language feature. The idiomatic uses are: ```go if v, ok := m[k]; ok { // key present; v is scoped to this if/else } if _, ok := m[k]; !ok { // key absent } v := m[k] // zero value is a fine default; presence does not matter here ``` The last form is not a mistake. Very often the zero value *is* the right default — `counts[word]++` works on an empty map precisely because the missing key reads as `0` and the whole element is then assigned back — and reaching for comma-ok when you do not need it is noise. ## Reading does not insert A point that trips people arriving from languages with auto-vivifying dictionaries: **a read never modifies the map.** `_ = m["absent"]` leaves `len(m)` unchanged and adds nothing. Insertion happens only through an assignment to `m[k]`, which includes the compound forms `m[k]++` and `m[k] += n` (both are defined as assigning the whole element). Symmetrically, `delete(m, k)` removes the entry if present and is a no-op if it is not — there is no error and no second return value to check. If you need to know whether you actually deleted something, do a comma-ok lookup first. ## Sets Go has no set type in the language, so a set is a map whose value carries no information. The two conventional spellings are `map[T]bool` and `map[T]struct{}`. With `map[T]bool` a one-value lookup is already a membership test, because a missing key reads as `false` — that is why the form is popular despite storing a byte per entry. With `map[T]struct{}` the value type is zero-width, so membership can only be asked with comma-ok: ```go seen := map[string]struct{}{} seen["doc-1"] = struct{}{} if _, ok := seen["doc-1"]; ok { // member } ``` Note what does *not* work: comparing the element to `struct{}{}` tests nothing, because every `struct{}` value compares equal to every other, including the zero value you get for a missing key. ## Values that are themselves nil-able When `V` is a slice, map or pointer, the zero value is `nil`, and again `nil` does not mean absent. `m["term"]` on a `map[string][]int` returns a nil slice both for a missing term and for a term explicitly stored with an empty posting list. A nil slice appends and ranges perfectly well, so code frequently gets away with ignoring the distinction — until it needs to report "we have never indexed this term" separately from "we indexed it and found nothing". That is the moment to switch to comma-ok. ## What interviewers are listening for They want the two-value form named correctly, the reason it exists (zero value versus absence), and the awareness that a plain read is side-effect-free. A candidate who says a missing key panics, returns `nil` regardless of value type, or inserts an entry has a mental model borrowed from another language.
- When is a plain one-value lookup the better choice?Whenever the zero value is the right default and presence does not change the behaviour. `counts[word]++`, `total += prices[id]` and `for _, doc := range index[term]` all read fine on a missing key, and adding a comma-ok check there is noise. Switch to comma-ok only when absent and zero must be treated differently.
- How does membership testing differ between map[string]bool and map[string]struct{}?With `map[string]bool`, `if set[k]` is enough: a missing key reads as `false`. With `map[string]struct{}` the value carries no information, so you must write `if _, ok := set[k]; ok`. Comparing the element to `struct{}{}` is useless — all `struct{}` values are equal, so it is true for absent keys too.
- For a map[string][]int, what does the comma-ok form return for a missing key?A nil slice and `false`. The zero value of a slice type is `nil`, so an absent term and a term stored with an empty posting list both read as nil. If "never indexed" must be reported differently from "indexed, no hits", the `ok` result is the only thing that distinguishes them.
Asking a Go map for a key is like asking a front desk for room 12's guest and always being handed a card: the card is blank both when the room is empty and when the guest's name is blank. The second return value is you also asking whether the room is occupied.
saying these in an interview costs you the question
- Says a lookup of a missing key panics
- Says a missing key always returns nil regardless of value type
- Thinks reading a missing key inserts a zero entry
- Treats the second return value as an error rather than a bool
- Uses len(m) or a loop over the map to test for one key