Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does append modify passed slice

Tags:

go

How could I iterate through the slice and pass somewhere the slice except the current element? Seems append() function modifies the underlying slice as we could see in documentation. But anyway I still don't know how to reach this.

func main() {
    args := []string{ "2", "3", "8" }

    for i, _ := range args {
        fmt.Println(append(args[:i], args[i+1:]...)) // or pass to function
    }

    fmt.Println(args)
}

result:

[3 8]
[3 8]
[3 8]
[3 8 8] // it is args now

what I expect:

 [3 8]
 [2 8]
 [2 3]

I already saw this Why does append() modify the provided slice? (See example)

but what is the Capacity of the slice is the secret for me, and I dont understand why did I exceed it.

like image 574
James May Avatar asked Mar 10 '16 15:03

James May


People also ask

Does append return a new slice?

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice.

Can you append to nil slice?

Appending to nil slice: As we know that zero value slice type is nil and the capacity and the length of such type of slice is 0. But with the help of append function, it is possible to append values to nil slice.

How slice append works in Golang?

The function takes the name of the slice to append to and the element(s) to append to the slice as the arguments. The function will return a slice with the original values and the new ones appended to the slice.

Can you append a slice to a slice Golang?

Two slices can be concatenated using append method in the standard golang library.


1 Answers

Performance is the big reason. Creating a new slice and copying all the elements over to it is expensive, so the slice code doesn't copy without good reason. However, if you exceed the slice's capacity, it grows by a suitable amount by copying the underlying slice. That means that the slice that's returned from append may not be the same slice you passed in.

The preferred way to use is:

args = append(args, newarg)

If you take a subslice, the capacity stays the same but your view into the slice changes. That means the missing elements are still there but outside the bounds of the new slice.

This explains the odd output of your code. You're printing the result of append each time but not storing that result, which means args isn't the same as what you printed.

The initial args slice is 3 elements big. For each index i - which is to say for 0, 1 and 2 - you take a subslice args[:i] and append all the elements of the remainder of the array args[i+1:] to it. That means that for:

i    args[:i]     args[i+1]...   Result         args
0    {}           {"3", "8"}     {"3", "8"}     {"3", "8", "8"}
1    {"3"}        {"8"}          {"3", "8"}     {"3", "8", "8"}
2    {"3", "8"}   {}             {"3", "8"}     {"3", "8", "8"}

tl;dr you should always save the result of append, and if you want to make a copy so you can change it then make a copy yourself.

like image 60
Marcus Downing Avatar answered Oct 20 '22 22:10

Marcus Downing