Go Slices: Difference between revisions
Line 190: | Line 190: | ||
:[[Go_Concepts_-_Functions#Varidic_Functions|Variadic Functions]] | :[[Go_Concepts_-_Functions#Varidic_Functions|Variadic Functions]] | ||
</blockquote> | </blockquote> | ||
=Three Index Slices= |
Revision as of 20:23, 11 April 2016
External
- Go Specification - Appending and Copying Slices https://golang.org/ref/spec#Appending_and_copying_slices
- Arrays, slices (and strings): The mechanics of 'append' https://blog.golang.org/slices
- Slice tricks: https://github.com/golang/go/wiki/SliceTricks
Internal
Overview
A slice is a reference type that implements a dynamic array. Slices are indexable, and they have a variable length. They are always associated with an underlying array, and the slice length cannot be longer than the underlying array - but it can be shorter. The slice's capacity is equal to the length on the underlying array. The index operator does not work beyond the capacity of the slice, but the slice can be expanded beyond the length of the underlying array by creating a new, longer underlying array - and consequently a new slice - with append().
Lexically, a slice type is a reference type. The slice instances must be initialized before attempting to use into them, either by using the make() function or a slice literal, otherwise you will get a runtime error, because a zero value for a slice is nil. More on nil slices: nil Slices and Empty Slices.
Implementation Details
The slices are three-field data structures that contain the metadata needed to manipulate the underlying array.
Accessing the Slice's Underlying Array
TODO, process this: http://stackoverflow.com/questions/25551082/how-would-you-access-the-underlying-array-passed-to-a-function-expecting-an-empt
Declaration
Long Declaration
A slice declaration is similar to an array's except the length is not specified. The slice is created with a zero length if no literal is specified.
var s []int
A slice declaration with a literal:
var s []int = []int {1, 2, 3}
Short Declaration
Short Declaration with Literals
A short declaration using a slice literal. The declaration is similar to declaring an array, except that the brackets do not contain the length:
s := []int{1, 2, 3}
The above declaration sets the length and capacity to 3.
A different literal format sets the length and the capacity by initializing only the last element of the slice. The following declaration creates a slice that has a length and capacity of 10:
s := []int{9: 0}
Short Declaration with make()
A short declaration can also be performed using make(). make() takes the length and the capacity of the slice.
Short Declaration with a Slice Expression
The slices can also be created with the "slice" expression applied to an array:
<array_identifier>[<low>:<high>]
Example:
var a [5]int b := a[1:4]
The "low" index is the index where to start the slice and "high" is the index where to end the new slice: the new slice will not contain the element indicated by the "high" index. The length of the new slice is given by the formula high - low. However, the capacity of the new slice will be determined by the boundary of the shared underlying array, so it will be len(underlying_array) - low. The same result is yielded by cap(a) - low.
- Note that after applying the slicing operation, the resulted slice shares a portion of the underlying array with the initial slice, so changing an element in one slice is reflected in the other.
Special forms of the "slice" expression:
- a[i:] is equivalent with a[i:len(a)].
- a[:i] is equivalent with a[0:i]
- a[:] is equivalent with a[0:len(a)]
nil Slices and Empty Slices
Declaring a slice without initializing it creates a nil slice - the slice variable is initialized with nil. This is common behavior for reference types:
var s []int
For a nil slice, fmt.Printf("%p", s) prints 0x0. The underlying data structures are initialized as follows: the array pointer is initialized to nil, the length is initialized to 0 and the capacity is initialized to 0.
A nil slice is different from an empty slice:
s := []int{}
s := make([]int, 0)
For an empty slice, fmt.Printf("%p", s) prints a pointer: 0x19d730. An empty slice points to a zero length array. The length is initialized to 0 and the capacity is initialized to 0.
Regardless of whether a nil or empty slice is used, the built-in functions append(), len() and cap() work the same.
Slice Operators and Functions
Indexing Operator
Indexing operator [] works in the same way as with arrays. The indexing operator can be used to access and change values of individual elements.
A slice an only access indexes up to its length, an attempt to access an element outside the slice's length will cause a runtime error. If the slice's capacity exceeds its length, the elements associated to the capacity that do not belong to the slice cannot be accessed via the index operator: they're only available for growth (see append()) and an attempt to access them via the index operator will also cause a runtime error.
Slice Length
len() returns the length of the slice.
Slice Capacity
cap() returns the length of the underlying array.
make()
The make() function creates the slice and the underlying array.
<slice_identifier> := make([]<slice_element_type>, <slice_length>, [capacity])
s := make([]int, 5) // the length and capacity are equal s1 := make([]int, 5, 10)
It is not allowed to create a slice with a capacity smaller than its length, the compiler will throw an error.
Note that make() returns the slice instance, not a pointer.
append()
append() adds element at the end of the slice, increasing its length, and, if necessary, the capacity:
<new_slice_identifier> := append(<old_slice_identifier>, <new_element>)
The addition is operated by writing the values in the corresponding positions in the underlying array, unless the array is not long enough. In this case, a new array long enough to accommodate the new elements is created and the original array values are copied across.
- The function always creates a new slice instance, don't expect that a lateral effect will change your input slice.
Example:
bs := make([]byte, 0) // the length of bs is 0 bs2 := append(bs, byte(1)) // the length of bs stays 0 // but the length of bs2 (a new slice) is 1
copy()
copy(dest_slice, source_slice)
The function copies all the source elements over the corresponding elements in the destination, overwriting the destination. If the lengths of the slices are not the same, the shortest one will be used.
Slice Expression
See Short Declaration with a Slice Expression above.