As Peter said, reslicing—whether using the old s[low:high]
syntax, or the new s[low:high:max]
syntax—never changes the underlying array itself. (The Go specification calls the new syntax with the max
expression added a full slice expression.)
The main thing to watch out for with slices also helps you to think about using slices. Remember that with any slice, there are really two parts:
- there is a slice header, which keeps a pointer to the array and provides the length and capacity; and
- there is some underlying array, somewhere.
You can provide the underlying array yourself:
var space [10]int
someSlice := space[:]
or you can use make
(or a function that itself calls make
) to have the runtime allocate the array somehow.1 Meanwhile, the compiler will allocate the slice header for you.
Calling any function, passing the slice as an argument, will pass it a copy of the slice header. The underlying array is still wherever it is, but the function you call gets a copy of the slice header. Note that in your own functions, you can inspect this slice header, directly or indirectly; see icza's answer to How to inspect slice header?.
Functions that you call might decide on their own: Gosh, this slice header talks about an array that's too small for the things I would like to put into the array. I'll allocate a new array somewhere, copy all the old array's values via the old slice header, and use the new slice header with the new array to do my work. The built in append
does exactly this.
A function that does do this sort of thing tends to do it not always, but only sometimes: sometimes, the slice header talks about an array that's already big enough, so it can just put more stuff in the array.
A function that does this will, in general, return for you a new slice header. You should grab this new value and use it. The new slice header will have a new pointer, length, and capacity. The new pointer may be the same as the old pointer! If so, watch out for this or any other function—perhaps an outer or inner recursive call—that tries to use either the old or the new slice header to modify the underlying array, because that other function might not expect that modification.
If append
(or whatever function you called) decided that, gee, the old underlying array was too small, and therefore allocated a new one and copied data to it, whoever still has the old slice header is "safe" from any meddling anyone does to the underlying array using the new slice header. So the code might sometimes work, and then sometimes fail, depending on whether append
(or whatever) decided to re-use the existing array, or make a new one.
That's what you need to watch out for, and that's why you need to pay attention to who has which slice headers and what underlying arrays those headers might be using.
1There's a certain degree of "magic" behind this make
allocation, which in the end, is just some very careful use of the unsafe
package so that the compiler and runtime can collude to produce useful results. You are allowed to peek at the internals as the Go runtime source code is available to all, but the Go authors reserve the right to change the internals in the future, if the update makes Go code faster or better or whatever.