What does this loop print after reassigning the slice during range?
Predict the output and explain why.
func main() {
lst := []string{"a", "b", "c", "d"}
for k, v := range lst {
if k == 0 {
lst = []string{"aa", "bb", "cc", "dd"}
}
fmt.Println(v)
}
}
Explain the output, and what would change if you instead wrote lst[3] = "z".
It prints a b c d. range evaluates its operand once at the start and iterates over a copy of the slice header (pointer, len, cap). Reassigning lst to a brand-new slice only rebinds the variable — the loop keeps iterating the original backing array. If instead you mutated an element of the original backing array, e.g. lst[3] = "z", the loop would see it and print a b c z.
- ✗Thinking
rangere-reads the slice variable each iteration - ✗Believing reassigning the variable changes the backing array being iterated
- ✗Expecting a panic instead of stable iteration over the original array
- →Why does mutating
lst[3]show up in the loop but reassigninglstdoes not? - →What three fields are copied when
rangesnapshots the slice header?
What it prints
a b c d.
range lst evaluates its operand once at the start of the loop and works with a copy of the slice header — a three-field struct of pointer to backing array, len, and cap. The backing array itself is not copied, but the lst variable and the range iterator are now independent.
The line lst = []string{"aa", "bb", "cc", "dd"} builds a brand-new slice with a new backing array and rebinds the lst variable to it. The range iterator knows nothing about that — it keeps walking the original array ["a","b","c","d"].
This illustrates "everything in Go is passed by value": the slice header is copied, not the array.
If you mutate an element in place
for k, v := range lst {
if k == 0 {
lst[3] = "z" // mutate the original backing array
}
fmt.Println(v)
}
Now the edit lands in the very array the iterator walks → it prints a b c z.