go
40 lines · 7 steps
How Go slices grow and share memory
A close look at when append reuses a slice's backing array and when it quietly allocates a new one.
Explained by
highlit
1package main
2
3import "fmt"
4
5// growSlice demonstrates how append grows a slice's underlying array.
6// When len == cap, append allocates a new, larger backing array and
7// copies the existing elements, so the slice header's data pointer changes.
8func growSlice(n int) {
9 s := make([]int, 0)
10 prevCap := cap(s)
11
12 for i := 0; i < n; i++ {
13 s = append(s, i)
14 if cap(s) != prevCap {
15 fmt.Printf("len=%-3d cap grew %-3d -> %-3d\n", len(s), prevCap, cap(s))
16 prevCap = cap(s)
17 }
18 }
19}
20
21// sharedBackingArray shows that appending within capacity mutates the
22// shared backing array, while exceeding capacity detaches the slice.
23func sharedBackingArray() {
24 base := make([]int, 3, 5)
25 for i := range base {
26 base[i] = i + 1
27 }
28
29 view := append(base, 99) // fits in cap: writes into base's array
30 fmt.Println("base:", base, "len/cap:", len(base), cap(base))
31 fmt.Println("view:", view, "len/cap:", len(view), cap(view))
32
33 // Grow beyond cap: append reallocates, view no longer shares with base.
34 for i := 0; i < 4; i++ {
35 view = append(view, 100+i)
36 }
37 view[0] = -1 // does not affect base anymore
38 fmt.Println("after realloc base:", base)
39 fmt.Println("after realloc view:", view, "cap:", cap(view))
40}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1append only allocates a new backing array when length would exceed capacity; otherwise it writes in place.
- 2Two slices can share the same backing array, so writes through one can be visible through the other.
- 3Once append reallocates, the resulting slice is detached and mutations no longer propagate to the original.
Related explainers
go
package main import ( "errors"
Parsing and validating CLI flags in Go
cli-parsing
validation
error-handling
Intermediate
8 steps
go
package cache import ( "container/list"
Building a generic LRU cache in Go
lru-cache
generics
linked-list
Intermediate
8 steps
go
package model import ( "encoding/json"
Custom JSON marshaling in Go
json
serialization
interfaces
Intermediate
5 steps
go
func (h *TransactionHandler) ExportCSV(c *gin.Context) { ctx := c.Request.Context() filters := parseTransactionFilters(c)
Streaming a CSV export in Gin
streaming
csv-export
database-cursor
Intermediate
8 steps
go
package store import ( "database/sql"
Wrapping and inspecting errors in Go
error-handling
error-wrapping
sentinel-errors
Intermediate
8 steps
go
package main import ( "context"
Graceful HTTP shutdown in Go
graceful-shutdown
signals
goroutines
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/how-go-slices-grow-and-share-memory-explained-go-a5e4/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.