liveBook · Manning

Slice copy

As explained in 100 Go Mistakes and How to Avoid Them

A slice copy is the technique of copying selected elements into a separately allocated slice so the result does not retain the source array.

A slice copy puts selected elements in a separately allocated slice. Use one when retaining a small part of a large slice would keep its backing array alive, or when later writes must not affect the original. section 3.10.1 section 3.9

Give copy a destination with length

Given `src := []int{0, 1, 2}`, `var dst []int; copy(dst, src)` copies nothing: the destination has length zero. Instead, use `dst := make([]int, len(src)); copy(dst, src)`. The result is `[0 1 2]`. `copy` takes the destination first and copies at most the shorter slice's length. `append([]int(nil), src...)` is a one-line alternative, though `copy` is clearer here. section 3.8

func getMessageType(msg []byte) []byte {
    msgType := make([]byte, 5)
    copy(msgType, msg)
    return msgType
}

To retain only a five-byte message type, allocate five bytes and copy into them. Returning `msg[:5]` instead can retain the entire received message's backing array; 1,000 retained prefixes of million-byte messages can therefore hold about 1 GB rather than about 5 KB. section 3.10.1

A full slice expression such as `msg[:5:5]` limits capacity but does not free the original backing array. It can limit where a later `append` writes, but it is not a substitute for a copy when the goal is to release a large source array. section 3.10.1 section 3.9

s := make([]int, 0, 1)
 
go func() {
    sCopy := make([]int, len(s), cap(s))
    copy(sCopy, s)                          #1
 
    s1 := append(sCopy, 1)
    fmt.Println(s1)
}()
 
go func() {
    sCopy := make([]int, len(s), cap(s))
    copy(sCopy, s)                          #2
 
    s2 := append(sCopy, 1)
    fmt.Println(s2)
}()

With spare capacity, two goroutines appending to slices backed by the same array can write the same location. Here each goroutine allocates and copies before appending, so their writes are isolated. section 9.9

Do not confuse copying elements with the slice copy made when `range` evaluates its expression. In `for range s`, the loop uses a temporary slice whose length stays fixed; that does not establish a separately allocated array of elements. section 4.2

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Works through destination-length rules and backing-array retention, including why reduced capacity alone does not release the array.
Examples
Contrasts a zero-length destination with an allocated one, then shows a five-byte message prefix copied out of a large message.
What it assumes
Assumes basic Go slice and goroutine use while explaining copy's destination-length rule and append's shared-array risk.
What you can do after
Build it from scratch with make and copy to retain small prefixes or isolate per-goroutine append operations.
How it connects
Connects slice copying to backing arrays, capacity, append, range loops, and data races.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.