Debugging Array Problems
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
Debugging array problems in Go is mostly reading the very precise signals the toolchain already gives you, in this order.
Start with compile errors, because several array bugs never reach runtime.
first argument to append must be slice means an array was used where a
slice's growth was intended — the fix is reconsidering the type, not the
call. mismatched types [3]int and [2]int means two different array types
met; the lengths in the message name the mismatch directly.
Runtime panics are equally legible. An off-by-one loop produces:
values := [3]int{10, 20, 30}
for i := 0; i <= len(values); i++ {
fmt.Println(values[i]) // panics when i reaches 3
}and the runtime states the exact failure: panic: runtime error: index out of range [3] with length 3. Index 3, length 3, therefore the last valid
index was 2 — the message is the whole diagnosis. Search the loop condition
for <=, the body for values[len(values)], and any recently changed
length arithmetic.
The silent class is non-mutation — the function ran, assigned, and the caller's array still prints the original value:
func setFirst(a [3]int) { a[0] = 99 }
// caller: [10 20 30] — the copy inside the function took the 99Confirm it by printing the array immediately before and after the call; if the value changes inside but not outside, you are seeing copy semantics. Decide deliberately: pointer parameter for mutation, slice for shared storage, or return the new value instead.
For inspection, fmt.Printf("%#v", x) is the array-debugging workhorse —
it prints [3]int{10, 20, 30}, type included, which immediately
distinguishes an array from a slice where %v would not. In Delve, set a
breakpoint at the faulting line, print x, print len(x), and read the
elements directly.
The short method: read the message (it names index and length), print
%#v at the boundary when behavior surprises you, and treat every
"didn't change" symptom as copy semantics until proven otherwise.
Debugging Array Problems
Direct Answer
Compile error about append or mismatched types: the bug is the data model
Deep Dive & Architecture
What makes array debugging teachable is that each failure class produces a different kind of evidence — compile-time, panic-time, or silent — and each class has a small, complete procedure.
Class one: errors before the program runs
Two compile errors dominate. The append error appears when code assumes a
growable collection but the variable is an array; the compiler's phrase
must be a slice is a data-model instruction. The mismatched-types error
appears when two array types of different lengths are assigned or
compared; because the length lives in the type, this is the same class of
error as assigning a string to an int. Both are caught before any
damage, which is exactly the protection array types exist to provide.
Class two: the panic with a complete story
The runtime error index out of range [3] with length 3 is unusually
generous: index attempted, actual length, and by arithmetic, the last legal
index. The three shapes that produce it:
i <= len(a)where<was meant — the loop takes one extra step.a[len(a)]— using the length as an index; the last index islen(a)-1.- A bound computed from a different array or a stale size — the panic's length tells you which array the runtime actually saw.
Delve at the panic line, then print i, print len(values) — the two
numbers in the message, verified live.
Class three: silence, the hardest one
A mutation that never lands produces no output at all — the program's data just stays the same. The procedure is to bracket the boundary:
fmt.Printf("before: %#v\n", x)
setFirst(x)
fmt.Printf("after: %#v\n", x) // unchanged → the function got a copy
If "before" and "after" match while an interior print inside the function
shows the change, copy semantics are confirmed. Then make a deliberate
choice rather than patching randomly: * if the function should mutate in
place, a slice if shared access is wanted, or a return value if the
cleanest Go style fits.
The inspection toolkit, in one run
Executed together, these are the prints that resolve most array questions at a glance:
x := [3]int{10, 20, 30}
fmt.Printf("%v\n", x) // [10 20 30] — the values
fmt.Printf("%#v\n", x) // [3]int{10, 20, 30} — values + the type
fmt.Println(len(x), cap(x)) // 3 3
%#v's inclusion of the type is what lets you catch the array/slice
confusion ([]int vs [3]int) that %v hides — and in a codebase where
[]int{...} and [...]int{...} look nearly identical at a glance, that
one format verb is the fastest disambiguator you have.
Code Example
x := [3]int{10, 20, 30}
fmt.Printf("%v\n", x) // [10 20 30] — values
fmt.Printf("%#v\n", x) // [3]int{10, 20, 30} — values + type
fmt.Println(len(x), cap(x)) // 3 3
Common Mistakes & Pitfalls
- Guessing at the panic instead of reading the index/length pair in the message.
- Not suspecting copy semantics when a function's changes 'disappear'.
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.