Concisely deep copy a slice?

Not sure which solution is fastest without a benchmark, but an alternative is using the built in copy:

cpy := make([]T, len(orig))
copy(cpy, orig)

From the documentation:

func copy(dst, src []Type) int

The copy built-in function copies elements from a source slice into a
destination slice. (As a special case, it also will copy bytes from a
string to a slice of bytes.) The source and destination may overlap.
Copy returns the number of elements copied, which will be the minimum
of len(src) and len(dst).

Note

The solution will copy all the values in the slice. If the slice contains pointers or structs with pointer fields, these pointer values will still point to the same values as the orig slice.

Benchmark

Benchmarking the two options, you can see they have very similar performance.

BenchmarkCopy     100000         24724 ns/op
BenchmarkAppend   100000         24967 ns/op
ok      benchmark   5.478s

This is the benchmark code:

package main

import "testing"

var result []T

const size = 10000

type T int

func BenchmarkCopy(b *testing.B) {
    orig := make([]T, size)

    for n := 0; n < b.N; n++ {
        cpy := make([]T, len(orig))
        copy(cpy, orig)
        orig = cpy
    }
    result = orig
}

func BenchmarkAppend(b *testing.B) {
    orig := make([]T, size)

    for n := 0; n < b.N; n++ {
        cpy := append([]T{}, orig...)
        orig = cpy
    }
    result = orig
}

I am not sure when/if the zero-fill is performed. But if you look at the assembly, in the append version you will have:

CALL    ,runtime.growslice(SB)

while the copy will call:

CALL    ,runtime.makeslice(SB)

and I would guess that both of these calls performs the zero-fill.

Leave a Comment