* Brian, son of Bob <brianwei...@gmail.com> [221102 00:49]: > Can anyone explain these gotchas (demo <https://go.dev/play/p/g40KMK-zsNk>)? > I've tried reading articles on this [one <https://go.dev/blog/slices-intro>, > two > <https://codeburst.io/a-comprehensive-guide-to-slices-in-golang-bacebfe46669>] > > but they don't go into enough detail.
Burak gave a very good, succinct answer to your question. I will give a more verbose explanation. [Short summary at bottom.] Go does not have a concept of "linked" slices, and does not use that terminology. Instead, slices have an _underlying array_, sometimes also called a slice's _backing array_. More than one slice can have the same backing array, so that changing an element of one slice might change an element of another slice. However, the underlying array for a slice can be changed at any time by assignment to the slice, as opposed to assignment to a slice element; and this does _not_ change the underlying array for other slices that currently use the original underlying array from the first slice. This is the primary difference between your concept of "linked slices" and the actual behavior of Go. If you think of a slice as a "view" into its (always fixed-size) underlying array, rather than a "variable sized array" you will probably have an easier time understanding its behavior. That is, the underlying array cannot change size, but the view into it can. var a = [10]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} // an array var s = a[1:5:7] // a slice with backing array a var t = a[3:6] // another slice with the same backing array var u = s[2:5] // like t, but limited by the capacity of s +---+---+---+---+---+---+---+---+---+---+ a | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | +---+---+---+---+---+---+---+---+---+---+ ^ ^ ^ s first last cap ^ ^ ^ t first last cap ^ ^ ^ u first last cap s[2] = 13 // now t[0] is 13 Assuming x is a slice that has length 5 and capacity 10, the results of the following expressions all have the same backing array: x x[:] x[0:] x[:len(x)] x[1:] x[:4] x[2:4] x[3:8] x[3:5:7] The playground link https://go.dev/play/p/tbm97ueCSdP demonstrates that all these have the same backing array by creating all the slices as named variables, then modifying one element of one of the slices, and showing that all the slices have had that corresponding element changed. In fact, all Slice Expressions as defined at https://go.dev/ref/spec#Slice_expressions in the Go Language Specification return a slice with the same underlying array as the original slice. There are only a small number of ways to create a slice: 1. A slice literal. 2. The built-in make function. 3. A slice expression (as defined in the language spec) applied to a slice, array, or pointer to array. 4. Assignment or argument passing or returning a result. 5. The built-in append function. Each of these (at least conceptually; the compiler can optimize away unnecessary allocations) creates a new slice value (i.e. internal structure containing pointer into underlying array, length, and capacity). For 1 and 2, a new underlying array is always created. For 3 and 4, the underlying array does not change. The tricky case is 5, where the append function can return a new slice with the same underlying array as its first argument, or it may create a new underlying array. This is where capacity comes in. If the original slice has enough capacity to hold all the appended elements, the backing array elements are modified and the result is a slice with the same backing array. If the original slice does not have enough capacity, a new backing array is created, and the old slice elements followed by the new slice elements (the remaining arguments to append) are copied into it. Note well that the expression «x = append(x, 9, 8, 7)» is not the same as a conceptual «x.append(9, 8, 7)»; that is, it is not appending to the variable named x, it is creating a new slice using the value of x and then assigning that new slice to x. Whether the new slice has the same underlying array as the original depends on the capacity of the original. In your original message, you say that x[0:] and x[:len(x)] are not "linked" to x. You are basing that on whether or not append(y, 4) modifies x. However, these do share the same backing array as x, but append(x[:len(x)], 4) does not (if len(x) == cap(x)). What you are seeing is that when len(x) == 3 and cap(x) == 3, and you assign y = x[:len(x)-1], then len(y) == 2, but cap(y) == 3, so y has enough capacity to append one element, but not two. The condensed takeaway: Go does not have any concept that remotely resembles what you are thinking of as "linked" slices. Slices are "views" into a fixed-size array. Different slices may be views into the same array. In such a case, changing an element of one slice will change all slices with views into the same array if their view contains the corresponding element. Append is not a method that modifies a slice directly. Append may or may not modify the original underlying array. ...Marvin -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/Y2KtXxWfkCZcJUbl%40basil.wdw.