Why can't I duplicate a slice with `copy()`?

前端 未结 6 697
情书的邮戳
情书的邮戳 2021-01-29 23:02

I need to make a copy of a slice in Go and reading the docs there is a copy function at my disposal.

The copy built-in function copies elements from a so

相关标签:
6条回答
  • 2021-01-29 23:07

    NOTE: This is an incorrect solution as @benlemasurier proved

    Here is a way to copy a slice. I'm a bit late, but there is a simpler, and faster answer than @Dave's. This are the instructions generated from a code like @Dave's. These is the instructions generated by mine. As you can see there are far fewer instructions. What is does is it just does append(slice), which copies the slice. This code:

    package main
    
    import "fmt"
    
    func main() {
        var foo = []int{1, 2, 3, 4, 5}
        fmt.Println("foo:", foo)
        var bar = append(foo)
        fmt.Println("bar:", bar)
        bar = append(bar, 6)
        fmt.Println("foo after:", foo)
        fmt.Println("bar after:", bar)
    }
    

    Outputs this:

    foo: [1 2 3 4 5]
    bar: [1 2 3 4 5]
    foo after: [1 2 3 4 5]
    bar after: [1 2 3 4 5 6]
    
    0 讨论(0)
  • 2021-01-29 23:10

    The copy() runs for the least length of dst and src, so you must initialize the dst to the desired length.

    A := []int{1, 2, 3}
    B := make([]int, 3)
    copy(B, A)
    C := make([]int, 2)
    copy(C, A)
    fmt.Println(A, B, C)
    

    Output:

    [1 2 3] [1 2 3] [1 2]
    

    You can initialize and copy all elements in one line using append() to a nil slice.

    x := append([]T{}, []...)
    

    Example:

    A := []int{1, 2, 3}
    B := append([]int{}, A...)
    C := append([]int{}, A[:2]...)
    fmt.Println(A, B, C)    
    

    Output:

    [1 2 3] [1 2 3] [1 2]
    

    Comparing with allocation+copy(), for greater than 1,000 elements, use append. Actually bellow 1,000 the difference may be neglected, make it a go for rule of thumb unless you have many slices.

    BenchmarkCopy1-4                50000000            27.0 ns/op
    BenchmarkCopy10-4               30000000            53.3 ns/op
    BenchmarkCopy100-4              10000000           229 ns/op
    BenchmarkCopy1000-4              1000000          1942 ns/op
    BenchmarkCopy10000-4              100000         18009 ns/op
    BenchmarkCopy100000-4              10000        220113 ns/op
    BenchmarkCopy1000000-4              1000       2028157 ns/op
    BenchmarkCopy10000000-4              100      15323924 ns/op
    BenchmarkCopy100000000-4               1    1200488116 ns/op
    BenchmarkAppend1-4              50000000            34.2 ns/op
    BenchmarkAppend10-4             20000000            60.0 ns/op
    BenchmarkAppend100-4             5000000           240 ns/op
    BenchmarkAppend1000-4            1000000          1832 ns/op
    BenchmarkAppend10000-4            100000         13378 ns/op
    BenchmarkAppend100000-4            10000        142397 ns/op
    BenchmarkAppend1000000-4            2000       1053891 ns/op
    BenchmarkAppend10000000-4            200       9500541 ns/op
    BenchmarkAppend100000000-4            20     176361861 ns/op
    
    0 讨论(0)
  • 2021-01-29 23:12

    Another simple way to do this is by using append which will allocate the slice in the process.

    arr := []int{1, 2, 3}
    tmp := append([]int(nil), arr...)  // Notice the ... splat
    fmt.Println(tmp)
    fmt.Println(arr)
    

    Output (as expected):

    [1 2 3]
    [1 2 3]
    

    So a shorthand for copying array arr would be append([]int(nil), arr...)

    https://play.golang.org/p/sr_4ofs5GW

    0 讨论(0)
  • 2021-01-29 23:13

    If your slices were of the same size, it would work:

    arr := []int{1, 2, 3}
    tmp := []int{0, 0, 0}
    i := copy(tmp, arr)
    fmt.Println(i)
    fmt.Println(tmp)
    fmt.Println(arr)
    

    Would give:

    3
    [1 2 3]
    [1 2 3]
    

    From "Go Slices: usage and internals":

    The copy function supports copying between slices of different lengths (it will copy only up to the smaller number of elements)

    The usual example is:

    t := make([]byte, len(s), (cap(s)+1)*2)
    copy(t, s)
    s = t
    
    0 讨论(0)
  • 2021-01-29 23:19

    The Go Programming Language Specification

    Appending to and copying slices

    The function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. Both arguments must have identical element type T and must be assignable to a slice of type []T. The number of elements copied is the minimum of len(src) and len(dst). As a special case, copy also accepts a destination argument assignable to type []byte with a source argument of a string type. This form copies the bytes from the string into the byte slice.

    copy(dst, src []T) int
    copy(dst []byte, src string) int
    

    tmp needs enough room for arr. For example,

    package main
    
    import "fmt"
    
    func main() {
        arr := []int{1, 2, 3}
        tmp := make([]int, len(arr))
        copy(tmp, arr)
        fmt.Println(tmp)
        fmt.Println(arr)
    }
    

    Output:

    [1 2 3]
    [1 2 3]
    
    0 讨论(0)
  • 2021-01-29 23:33

    The builtin copy(dst, src) copies min(len(dst), len(src)) elements.

    So if your dst is empty (len(dst) == 0), nothing will be copied.

    Try tmp := make([]int, len(arr)) (Go Playground):

    arr := []int{1, 2, 3}
    tmp := make([]int, len(arr))
    copy(tmp, arr)
    fmt.Println(tmp)
    fmt.Println(arr)
    

    Output (as expected):

    [1 2 3]
    [1 2 3]
    

    Unfortunately this is not documented in the builtin package, but it is documented in the Go Language Specification: Appending to and copying slices:

    The number of elements copied is the minimum of len(src) and len(dst).

    Edit:

    Finally the documentation of copy() has been updated and it now contains the fact that the minimum length of source and destination will be copied:

    Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).

    0 讨论(0)
提交回复
热议问题