Slices and Arrays
Explore how Go's slices and arrays function, their differences in memory handling, and why slices are preferred for flexible data management. Understand how passing slices affects underlying data and how to avoid common pitfalls with slicing.
In Go, slices and arrays serve a similar purpose. They are declared nearly the same way:
Slices feel like arrays with useful functionality on top. They use pointers to arrays internally in their implementation. Slices however are so much more convenient that arrays are rarely used directly in Go.
Arrays
An array is a typed sequence of memory of a fixed length. Arrays of different lengths are considered to be different incompatible types. Unlike in C, array elements are initialized to zero values when an array is created so there’s no need to do that explicitly.
Also unlike in C, a Go array is a value type. It’s not a pointer to the first element of a block of memory. If you pass an array into a function, the whole array will be copied. You can still pass a pointer to an array to not have it copied.
Slices
A slice is a descriptor of an array segment. It’s a very useful data structure, but perhaps slightly unusual. There are several ways to shoot yourself in a foot with it, all of which can be avoided if you know how a slice works internally. Here’s the actual definition of a slice in Go source code:
This has interesting implications. A slice itself is a value type, but it
references the array it uses with a pointer. Unlike with an array, if you
pass a slice to a function you would get a copy of array pointer, len,
and cap properties (the first block in the image above), but the data
in the array itself wouldn’t be copied. Both copies of the slice would point to the same array. The same thing happens when you “slice” a
slice. Slicing creates a new slice, which still points to the same array:
If you’re unaware of what a slice is, you might assume that it’s a value type, and be surprised that f1 “corrupted” the data in the slice in main.