-
Arrays
Arrays are not often seen in Go programs because the size of an array is part of its type, which limits its expressive power.
The declaration
var buffer [256]byte
declares the variable buffer,which holds 256 bytes.
We can access its elements with the familiar indexing syntax,buffer[0], buffer[1] and so on through buffer[256].
There is a built-in function called len that returns the number of elements of an array or slice and also of a few other data types.
Arrays are a good representation of a transformation matrix for instace, but their most common purpose in Go is to hold storage for a slice.
-
The slice header
A slice is a data structure describing a contiguous section of an array stored separately from the slice variable itself. A slice describes a piece of an array.
Given our buffer array variable from the previous sevtion, we could create a slice that describes emements 100 through 150 by slicing the array:
var slice []byte = buffer[100:150]
var slice = buffer[100:150]
slice := buffer[100:150]
What exactly is the slice variable?It's not quite the full strory,but bow think of the slice being built like this behind the scenes:
type sliceHeader struct {
Length int
ZerothElement *byte
}
slice := sliceHeader {
Length: 50,
ZerothElement: &buffer[100]
}
Despite what this snippet says that sliceHeader struct 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.
-
Passing slices to functions
It's important to understand that even though a slice contains a pointer,it is itself a value.Under the covers,it is a struct value holding a pointer and a length.It's not a pointer to a struct.
When we called IndexRune in the previous example,it was passed a copy of the slice header.
func AddOneToEachElement(slice []byte) {
for 1:= range slice {
slice[i]++
}
}
func main() {
slice := buffer[10:20]
for i := 0; i < len(slice); i++ {
slice[i] = byte(i)
}
fmt.Println("before", slice)
AddOneToEachElement(slice)
fmt.Println("after",slice)
}
the result:
before [0 1 2 3 4 5 6 7 8 9]
after [1 2 3 4 5 6 7 8 9 10]
Even though the slice header is passed by value,the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array.Therefore, when function returns, the modified elements can be seen through the original slice variable.
The argument to the function really is a copy, as this example shows:
func SubtractOneFromLength(slice []byte) []byte {
slice = slice[0 : len(slice)-1]
return slice
}
func main() {
fmt.Println("Before: len(slice) =", len(slice))
newSlice := SubtractOneFromLength(slice)
fmt.Println("After: len(slice) = ", len(slice))
fmt.Println("After:len(newSlice) =", len(newSlice))
}
the result:
Before: len(slice) = 10
After: len(slice) = 10
After:len(newSlice) = 11
Here we see that the contents of a slice argument can be modified by a function, but its header cannot. The length stored in the slice variable is not modified by the call to the function, since the function is passed a copy of thr slice header, not the original. Thus if we want to write a function that modifies the header, we must return it as a result parameter, just as we have done here. The slice variable is unchanged but the returned value has the new length, which is then stored in newSlice.
-
Pointers to slices:Method recievers
Anthor way to have a function modify the slice header is to pass a pointer to it. Here's a variant of our previous example that does this:
type path []byte
func (p path) ToUpper() {
for i, b := range p {
if 'a' <= b && b <= 'z' {
p[i] = b + 'A' - 'a'
}
}
}
func main() {
pathName := path("/usr/bin/tso")
pathName.ToUpper()
fmt.Printf("%s\n", pathName)
}
-
Capacity
Besides the array pointer and length, the slice header also stores its capacity:
type sliceHeader struct {
Length int
Capacity int
ZerothElement *byte
}
The Capacity field records how much space the underlying array actually has; it is the maximum value the Length can reach.
-
Make
We can't grow the slice beyond its capacity,but we can achieve an equivalent result by allocating a new array, copying the data over, and modifying the slice to describe the new array.
We could use the new built-in function to allocate a bigger array and then slice the result, but it is simpler to use the make built-in function instead.
slice := make([]int, 10, 15)
newSlice := make([]int, len(slice), 2*cap(slice))
for i := range slice {
newSlice[i] = slice[i]
}
slice = newSlice
-
Copy
Go has a built-in function, copy, which copies the data from the right-hand argument to the left-hand argument.The number of elements it copies is the minimum of the lengths of the two slices. Copy returns an integer value, the number of elements it copied.
newSlice := make([]int, len(slice), 2*cap(slice))
copy(newSlice,slice)
-
Append: The built-in function
//Append appends the elements to the slice
//Efficient version
func append(slice []int, elements ...int) []int {
n := len(slice)
total := len(slice) + len(elements)
if total > cap(slice) {
newSize := total*3/2 + 1
newSlice := make([]int, total, newSize)
copy(newSlice, slice)
slice := newSlice
}
slice = slice[:total]
copy(slice[n:], elements)
return slice
}
Go provides a built-in generic append function. It works the same as out int slice version, but for any slice type.
slice := []int{1,2,3}
slice2 := []int{55,56,57}
slice = append(slice, 4)
slice2 = append(slice, slice2...)
slice3 := append([]int(nil), slice...)
slice = append(slice, slice)
-
Nil
A nil slice is the zero value of the slice header:
sliceHeader{
Length: 0,
Capacity: 0,
ZerothElement: nil,
}
or just sliceHeader{}
.
The slice created by array[0:0]
.
-
Strings
Strings are just read-only slices of bytes with a bit of extra syntactic support from the language.Because they are read-only, there is no need for a capacity (you can't grow them), but otherwise for most purposes you can treat them just like read-only slices of bytes.
We can also take a normal slice of bytes and create a string from it with the simple conversion:
str := string(slice)
and go in reverse direction as well:
slice := []byte(str)