According to the Tour of Go, in a Go slice s
, the expression s[lo:hi]
evaluates to a slice of the elements from lo
through hi-1<
This is completely a matter of convention, and there are certainly other ways to do it (for example, Matlab uses arrays whose first index is 1). The choice really comes down to what properties you want. As it turns out, using 0-indexed arrays where slicing is inclusive-exclusive (that is, a slice from a to b includes element a and excludes element b) has some really nice properties, and thus it's a very common choice. Here are a few advantages.
Advantages of 0-indexed arrays and inclusive-exclusive slicing
(note that I'm using non-Go terminology, so I'll talk about arrays in the way that C or Java would talk about them. Arrays are what Go calls slices, and slices are sub-arrays (ie, "the slice from index 1 to index 4"))
arr
, arr[0:len(arr)]
is just arr
itself. This comes in handy a lot in practice. For example, if I call n, _ := r.Read(arr)
(where n
is the number of bytes read into arr
), then I can just do arr[:n]
to get the slice of arr
corresponding to the data that was actually written into arr
.Indices don't overlap. This means that if I have arr[0:i]
, arr[i:j]
, arr[j:k]
, arr[k:len(arr)]
, these slices fully cover arr
itself. You may not often find yourself partitioning an array into sub-slices like this, but it has a number of related advantages. For example, consider the following code to split an array based on non-consecutive integers:
func consecutiveSlices(ints []int) [][]int {
ret := make([][]int, 0)
i, j := 0, 1
for j < len(ints) {
if ints[j] != ints[j-1] + 1 {
ret = append(ret, ints[i:j])
i = j
}
}
ret = append(ret, ints[i:j])
}
(this code obviously doesn't handle some edge cases well, but you get the idea)
If we were to try to write the equivalent function using inclusive-inclusive slicing, it would be significantly more complicated.
If anyone can think of any more, please feel free to edit this answer and add them.