Four Real Array Traps
Fast recall
Quick revision
Core answer
append does not work on arrays — it requires a slice. a = append(a, 4)Interview-ready guide
Interview answer
Most array mistakes in Go come from expecting slice behavior, so they are worth walking through as four specific traps.
The first is calling append on an array. append grows a slice by
possibly reallocating its backing storage — an array has no indirection to
reallocate, so this fails before the program runs:
a := [3]int{1, 2, 3}
a = append(a, 4) // compile error: first argument to append must be sliceThe fix is not to make it work but to ask why you are growing a fixed-length value; if growth is real, the data wants to be a slice.
The second is the classic off-by-one. Valid indices run from 0 to
len(values)-1, so i <= len(values) walks one step too far:
for i := 0; i <= len(values); i++ {
fmt.Println(values[i])
}This compiles and then panics the moment i hits 3, with the runtime
telling you everything: panic: runtime error: index out of range [3] with length 3. The index and the length in that message are the two numbers
you need.
The third is mutation-by-proxy. A function that takes [3]int receives a
copy, so assignments inside it are invisible to the caller:
func setFirst(a [3]int) { a[0] = 99 }
x := [3]int{10, 20, 30}
setFirst(x)
fmt.Println(x) // [10 20 30] — the 99 only touched the copyThe program prints [10 20 30], not [99 20 30]. If you want the function
to change the caller's data, take a pointer (a *[3]int) or a slice.
The fourth is literal confusion: []int{1,2,3} is a slice, [...]int{1,2,3}
is an array. The printed types make it concrete: []int versus [3]int.
Copy semantics, append, and comparability all flip on that one character.
All four traps reduce to the same sentence — an array is a fixed-length value — and saying that sentence, with these four failure modes attached, is the complete answer.
Four Real Array Traps
Direct Answer
append does not work on arrays — it requires a slice. a = append(a, 4)
Deep Dive & Architecture
Each of these mistakes is instructive because the compiler or runtime gives you a distinct, honest signal — learning to read those signals is the real skill this question tests.
Why append cannot exist for arrays
append's contract is "return a possibly-longer slice", which works because
a slice header (pointer, length, capacity) can be redirected to new storage.
An array is the storage itself, inline in the variable — there is nothing to
redirect and no header to update. So Go rejects the call at compile time
with first argument to append must be slice; have a (variable of type [3]int). That error text is doing you a favor: it is telling you your data
model is wrong, not your syntax.
Reading the range panic precisely
The panic index out of range [3] with length 3 contains a complete
diagnosis: the index you used, the length you had. The bug is always one of
three shapes — a <= where < was intended, a length used as an index
(values[len(values)]), or stale length assumptions after data changes.
Checking which shape it is takes seconds once you read the two numbers.
The silent one: copies that "don't work"
The mutation trap is the most dangerous of the four because there is no error at all — the program simply continues with the original data. The defense is knowing the boundary rule: everything in Go passes by value, and for arrays the value is the whole block. When silence is the failure mode, the fix is making the copy visible or changing the type: pointer for in-place mutation, slice for shared storage, or return the modified value instead of mutating a parameter.
One character, two data models
[]int and [3]int differ by one glyph and by everything that matters:
copy-on-assign versus header sharing, comparable versus not, fixed versus
growable. The ... in [...]int{1,2,3} is the only place Go lets the
compiler count for you, and the result is still a real array type — verified
by %T printing [3]int. When a program's behavior flips unexpectedly,
checking which literal was written is a good first move.
Code Example
// trap 1: append on an array — compile error
a := [3]int{1, 2, 3}
a = append(a, 4) // compile error: first argument to append must be slice
// trap 2: off-by-one — runtime panic
for i := 0; i <= len(values); i++ { // i reaches 3, array ends at 2
fmt.Println(values[i])
}
// trap 3: mutation through a copy
func setFirst(a [3]int) { a[0] = 99 }
x := [3]int{10, 20, 30}
setFirst(x)
fmt.Println(x) // [10 20 30] — the 99 only touched the copy
Common Mistakes & Pitfalls
- Trying to append to an array instead of using a slice.
- Writing i <= len(values) and being surprised by the range panic.
- Expecting function parameters to mutate the caller's array.
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.