0

When running the following program:

package main

import "fmt"

func main() {
    edges := [][]int{{1,2}, {2,3}, {3,4}, {1,4}, {1,5}}
    printSlice2d(edges)

    _ = append(edges[:0], edges[1:]...)
    printSlice2d(edges)
}

func printSlice2d(s [][]int) {
    fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}

I get the following output:

len=5 cap=5 [[1 2] [2 3] [3 4] [1 4] [1 5]]
len=5 cap=5 [[2 3] [3 4] [1 4] [1 5] [1 5]]

I don't understand why edges is being changed by calling append. I would expect the first and second lines to be the same. I've checked the specification, but I can't seem to find anything that would explain this behaviour this kind of input.

2 Answers2

1

edges[:0] is a slice of length 0 starting at index 0 of the underlying array of edges.

To this slice, you append another slice, the slice of length 4 starting at index of the underlying array ofedges`. That gives you the first 4 elements of the result you see in the second line.

Then you print edges, which is a slice with an underlying array of 5 elements, whose last 4 you just shifted one element lower. The last element is duplicated. in the edges array.

If you look at the result of the append, then you'd see a slice with length 4, cap 5, the first 4 elements of the underlying array of edges.

If you expected the two lines to be the same, maybe you tried to do:

append(edges[:1],edges[1:]...)
zerkms
  • 249,484
  • 69
  • 436
  • 539
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
1

Slice uses array internally to store the data.

append function does not create a new array if it is not necessary to increase array size beyond its current capacity. Instead it copies new data elements into existing array. After that function returns reference to a new slice that internally uses the same array.

You can read more in this article - https://blog.golang.org/go-slices-usage-and-internals

As mentioned earlier, re-slicing a slice doesn't make a copy of the underlying array. The full array will be kept in memory until it is no longer referenced. Occasionally this can cause the program to hold all the data in memory when only a small piece of it is needed.

This is what is going on in this line:

_ = append(edges[:0], edges[1:]...)
  1. second append function argument (edges[1:]...) copies 4 items of edges into temp var. Its value - [{2,3}, {3,4}, {1,4}, {1,5}]
  2. these values are copied into array that edges uses internally to store data. That overrides all items except of last one. This is where edges is mutated.
  3. append returns reference to a new slice that internally uses the same array to store the data as edges
  4. returned slice is ignored and will be garbage collected, but that does not matter for edges.

That is why you see changed values when you check edges after performing append on it.

Dmitry Harnitski
  • 5,838
  • 1
  • 28
  • 43
  • 1
    Append doesn't return pointers. Array sizes are fixed, so it can't "increase [the] size of current array". It will indeed allocate a new array if the underlying array is too small to fit the values that are to be appended. That's all described in the blog post you linked. – Peter Jan 05 '20 at 09:39
  • @pete thanks for your comment. I will update the answer. – Dmitry Harnitski Jan 05 '20 at 14:16