Underlying array of slice

tail := make([]byte, 1000, 1000)
block := make([]byte, 1000)
block = append(tail, block...)

In the above code “tail” and “block” would be pointing to different underlying arrays

tail := make([]byte, 1000, 2000)
block := make([]byte, 1000)
block = append(tail, block...)

In the above code, the last line would append 1000 bytes from “block” into “tail” and
then point “block” to “tail”. So, in this case, “tail” and “block” would be pointing to the same
underlying arrays.

How can I programmatically find out if the underlying array of 2 slices point to the same
memory location.

if &tail[0] == &block[0] {
// same location
}

The above code works but is it idiomatic Go code? Is there a better way?

1 Like

Why are you checking if a slice shares the same underlying array as another? Would you also want to handle a situation like this?

a := make([]byte, 1000)
b := a[16:24]

Let’s say “tail” and “block” are byte slices. The code below appends bytes from “block”, adds it to “tail” and then copies over that slice into “block”.

block = append(tail, block...)

Before this line of code, it is possible that “tail” has enough capacity to hold all bytes from “block”. In this case, no new memory will get allocated and after this line of code, both “tail” and “block” will point to the same underlying array.

If “tail” does not have enough capacity to hold all bytes from “block”, new memory will get allocated and “block” will point to that. In this case, both “tail” and “block” will point to different underlying array.

The same function can be called again and we might append bytes to “tail”. We have to nil out “tail” to make sure we do not overwrite “block” above.

block = append(tail, block...)
tail = nil

If “tail” and “block” were pointing to same locations, the underlying array is still referenced by “block” and would not get garbage collected.

If “tail” and “block” were pointing to different locations, niling out “tail” means the underlying array would get garbage collected. The next time this function gets called and we append bytes to “tail”, we would allocate new memory location. We should be able to optimize this.

block = append(tail, block...)
if &tail[0] == &block[0] {
   // same location
   tail = nil
} else {
   // different location
   tail = tail[:0]
}

The above code makes sure memory for the underlying array for “tail” is not freed unnecessarily. The “if” check above has to be accurate, hence this question.

In the code snippet you have byte slices “a” and “b” share the same underlying array, however “b” is using a subset to array for “a”. This is not what I am looking for, however I also would like to know if we can programatically find out if “a” and “b” share the same underlying array. This is for some other use case.

The better option here is usually to have a policy for whether a functions “owns” a slice passed to it or needs to make a copy if it wants to retain it. For example io.Reader and friends documented as not retaining the slice. Also, using the three index slice (foo[:len(foo):len(foo)]) to limit the capacity, enforcing that appends become new allocations. A function given such a slice can’t overwrite bytes after len.