Append
Arrays
The size of an array is part of its type. This limits the expressive power a bit. However, sometimes limiting expression is what one wants, just like a type system.
The type of buffer includes its size, [256]byte
. An array with 512 bytes would be of the distinct type [512]byte
.
Arrays have their place, they are a good representation of a transformation matrix for instance, but their most common purpose in Go is to hold storage for a slice.
Slices
A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself.
A slice is not an array. A slice describes a piece of an array.
We can slice the buffer to create a slice.
What is a slice? It's not quite the full story but think of it as a data structure with two elements, a length and a pointer to an element of an array.
The slice header is not visible to the programmer, and the type of the element pointer depends on the type of the elements, but this gives the general idea of the mechanics.
We can slice a slice.
This operation creates a new slice, in this case with elements 5 through 9 of the original slice, which means elements 105 through 109 of the original array. The underlying slice header would look like this.
The header is still pointing to the same underlying array, stored in the buffer variable.
Slice Passes as Value
Although slice contains a pointer, it is itself a value. Under the covers, it is a struct value holding a pointer and a length. It is NOT a pointer to a struct.
Passing a slice to a function is essentially copying a slice header. I am using slice and slice header interchangeably because it's the language the original Go post uses.
Slice Passes as Reference
Similar to previous example, instead of returning a new slice, we can modify the original slice if we accept a pointer.
Let's say we wanted to have a method on a slice that truncates it as the final slash.
Capacity
Besides the array pointer and length, the slice header also stores its capacity. The capacity field records how much space the underlying array actually has; it is the maximum value the length can reach. Trying to grow the slice beyond its capacity will step beyond the limits of the array and will trigger a panic.
The header looks like this.
Make
If we want to grow a slice beyond its capacity, we need to define a new underlying array and copy over the data. The built-in function make
allocates a new array and creates a slice header to describe it.
Copy
Go has a built-in function to copy arrays. The copy function is smart. It only copies what it can, paying attention to the lengths of both arguments. In other words, the number of elements it copies is the minimum length of the two slices.
Assuming a slice has enough room for new element, an insert function would write like this.
Append
We have enough ingredients to build the append
function.
We can make the append
more efficient by allocating no more than once.
Built-in Append
Since slice header is always updated by a call to append
, we need to save the returned slice after the call. In fact, the compiler won't let us call append without saving the result!
All slice tricks can be found here https://github.com/golang/go/wiki/SliceTricks.
Nil Slice
Nil slice is essentially the zero value of slice header.
String
Strings are read-only slices of bytes with a bit of extra syntactic support from the language. They are read-only, there is no need for a capacity because we can't grow them anyways.
The array underlying a string is hidden from view; there is no way to access its contents except through the string. That means that when we do string to bytes conversion, a copy of the array must be made. Modification of the array underlying the byte slice don't affect the corresponding string.
Last updated