Common Pitfalls When Using Go Slices and How to Avoid Them
This article explains three typical pitfalls of Go slices—ignoring the slice returned by append, unintentionally sharing the underlying array which can cause memory leaks, and the value‑copy behavior of for‑range loops—while also showing how to inspect slice internals using unsafe pointers.
In Go, slices are built on top of dynamic arrays and are widely used like Java's ArrayList , but developers must be aware of several subtle issues.
Pitfall 1: Using append without assigning the returned slice – because append may allocate a new underlying array, the original slice reference can become stale, causing added elements to be invisible.
Example (illustrated with screenshots in the original article) shows that the new slice returned by append must replace the old one.
Pitfall 2: Slice slicing shares the underlying array, leading to memory‑leak risk – similar to Java 7's String.substring implementation, a small slice that still references a large backing array prevents the garbage collector from reclaiming the large memory.
The article demonstrates this with visual examples and notes that Java solved the issue with System.arraycopy ; in Go you should explicitly copy data using the copy function.
Pitfall 3: The loop variable in a for range iteration is a copy – modifying the loop variable does not affect the original slice element.
Code snippets show the output before and after the modification, confirming that the original slice remains unchanged.
Understanding the slice internals
The underlying data structure is type SliceHeader struct { Data uintptr; Len int; Cap int } . By converting a slice to *reflect.SliceHeader via unsafe.Pointer , you can read the length, capacity, and the raw pointer to the backing array.
Sample code prints the slice contents, length, capacity, and accesses the first three elements directly through C‑style pointer arithmetic:
fmt.Println("C pointer first element:", *((*int)(unsafe.Pointer(p.Data))))
fmt.Println("C pointer second element:", *((*int)(unsafe.Pointer(p.Data + unsafe.Sizeof(p.Data)))) )
fmt.Println("C pointer third element:", *((*int)(unsafe.Pointer(p.Data + 2*unsafe.Sizeof(p.Data)))) )Summary
Go slices provide lightweight, efficient value semantics, but developers must remember to use the slice returned by append , avoid unintentionally sharing the backing array without copying, and be aware that for range variables are copies; otherwise subtle bugs and memory leaks may occur.
Cognitive Technology Team
Cognitive Technology Team regularly delivers the latest IT news, original content, programming tutorials and experience sharing, with daily perks awaiting you.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.