I'm trying to learn Golang using "The Go Programming Language" and I've reached the section on slices. They make the comparison between arrays and slices in that two arrays can be compared with ==
where two slices can not. The text reads as the following:
"== operator for arrays of strings, it may be puzzling that slice
comparisons do not also work this way. There are two reasons why deep
equivalence is problematic. First, unlike array elements, the elements
of a slice are indirect, making it possible for a slice to contain
itself. Although there are ways to deal with such cases, none is
simple, efficient, and most importantly, obvious."
What is meant by it's possible for a slice to contain itself due to the elements being indirect?
The below example creates a slice that contains itself:
This can be done because the slice value internally contains a pointer to an array, a length, and a capacity.
An array on the other hand is a value. It can, at best, contain pointers to itself.
Slice containing itself
Besides a recursive type (such as
type Foo []Foo
, see ANisus's answer) which is good for nothing besides demonstration, a slice may contain itself if for example the element type of the slice isinterface{}
:In this example the slice
s
will have 2 interface values, the first "wrapping" a simple string"one"
, and another interface value wrapping the slice value itself. When an interface value is created, a copy of the value will be wrapped which in case of slices means a copy of the slice header/descriptor, which contains the pointer to the underlying array, so the copy will have the same pointer value pointing to the same underlying array. (For more details about the representation of interfaces, see The Laws of Reflection: The representation of an interface.)If you were quickly on to print it:
You would get a fatal error, something like:
Because
fmt.Println()
tries to print the content recursively, and since the 2nd element is a slice pointing to the same array of the the slice being printed, it runs into an infinite loop.Another way to see if it really is the slice itself:
Output (try it on the Go Playground):
No matter how deep we go, the 2nd element will always be the slice value pointing to the same array as
s
, wrapped in aninterface{}
value.The indirection plays the important role as a copy will be wrapped in the
interface{}
but the copy will contain the same pointer.Array can't contain itself
Changing the type to be an array:
Output (try it on the Go Playground):
This is because when the array is wrapped into an
interface{}
, a copy will be wrapped - and a copy is not the original array. Sos
will have a second value, aninterface{}
wrapping an array, but that is a different array whose 2nd value is not set and therefore will benil
(the zero value of typeinterface{}
), so attempting to "go into" this array will panic because it isnil
(type assertion fails because not the special "comma, ok" form was used).Since this
s
array does not contain itself, a simplefmt.Println()
will reveal its full content:Output:
Further
interface{}
wrapping analysisIf you wrap an array in an
interface{}
and modify the content of the original array, the value wrapped in theinterface{}
is not affected:Output:
If you do the same with a slice, the wrapped slice (since points to the same underlying array) is also affected:
Output:
Try these on the Go Playground.
a slice contains a pointer to the memory holding the elements, a length for available elements count, and a capability for how big the memory. so it like:
I think it is
indirect
, because the elements are referenced by pointer. and of course we can have the slice itself invoid *data
.