Open atc0005 opened 5 years ago
The most known form of slice expression is:
input[low:high]
Indices low and high must be integers. They specify which elements of the operand (input) are placed inside the resulting slice or string. The result contains operand’s elements starting at low (inclusively) up to high (exclusively). Operand is either string, array, pointer to array or slice
https://www.packtpub.com/free-ebooks/learning-go-programming
The slicing expression uses the
[ : ]
operator to specify the low and high bound indices, separated by a colon, for the slice segment.
- The low value is the zero-based index where the slice segment starts
- The high value is the n th element offset where the segment stops
https://blog.golang.org/go-slices-usage-and-internals
The start and end indices of a slice expression are optional; they default to zero and the slice's length respectively
the expression b[1:4] creates a slice including elements 1 through 3 of b (the indices of the resulting slice will be 0 through 2).
http://shop.oreilly.com/product/0636920046516.do
Another way to create slices is to use the
[low : high]
expression:arr := [5]float64{1, 2, 3, 4, 5} x := arr[0:5]
low
is the index of where to start the slice andhigh
is the index of where to end it (but not including the index itself).For example, while
arr[0:5]
returns[1,2,3,4,5]
,arr[1:4]
returns[2,3,4]
.
https://www.manning.com/books/get-programming-with-go
Slicing is expressed with a
half-open range
. For example, in the following listing,planets[0:4]
begins with the planet atindex 0
and continues up to, but not including, the planet atindex 4
.
https://tour.golang.org/moretypes/7
A slice is formed by specifying two indices, a low and high bound, separated by a colon:
a[low : high]
This selects a half-open range which includes the first element, but excludes the last one.
The following expression creates a slice which includes elements 1 through 3 of a:
a[1:4]
From the Go Fundamentals course by Nigel Poulton:
package main
import (
"fmt"
)
func main() {
mySlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
// this was a "gotcha" shown in the course
fmt.Println(mySlice[4])
mySlice[1] = 0
fmt.Println(mySlice)
// Golang slices
// Left side: inclusive
// Right side: exclusive
sliceOfSlice := mySlice[2:5]
fmt.Println(sliceOfSlice)
}
From the https://www.udemy.com/course/go-the-complete-developers-guide course (paraphrased with Go code):
// https://play.golang.org/p/FIsOa4bRRXR
fruits := []string{"apple", "banana", "grape", "orange"}
// fruits[startIndexIncluding : upToNotIncluding]
fmt.Println(fruits[0:2])
Output:
[apple banana]
This scratch code is based off of https://github.com/mactsouk/mastering-Go-3rd/blob/9329a87dd5afc3878831e3a98739fa1c8c361cd5/ch03/phoneBook.go and attempts to reason why the starting or lower bound can be equal to or greater than the length of the slice:
package main
import (
"fmt"
)
func main() {
i := 2
data := []string{
"first", // index 0
"second", // index 1
"last", // index 2
}
fmt.Println("cap:", cap(data))
fmt.Println("len:", len(data))
fmt.Println("i:", i)
fmt.Println("data[:i]:", data[:i])
fmt.Println("data[i+1:]:", data[i+1:])
// Everything up to this makes sense, but not the following. Why are you
// able to specify a starting index one past the length of the slice
// without running into an index error?
fmt.Println("Start with index 3, one past slice length:")
fmt.Println("data[3:]:", data[3:]) // this works
// fmt.Println(data[3]) // this panics
// remove the slice element at specified index
// data = append(data[:i], data[i+1:]...)
data = append(
data[:2], // [first second]
data[3:]..., // []
)
// show results
fmt.Println(data)
}
and is explained by https://stackoverflow.com/questions/45299185/slice-start-position-greater-than-length-of-the-string:
Since you are slicing a string, indices are in range if:
0 <= low <= high <= len(a)
This expression:
"hi"[2:]
Since the upper bound is missing, it defaults to length, which is 2, so it is equvivalent to:
"hi"[2:2]
This is perfectly valid by the spec, and it will result in an empty string. If you change it to
"hi"[3:]
, then it will be out of range and result in a compile-time error (as slicing a constant string can be checked at compile-time).Reasoning is that the upper bound is exclusive, e.g.
a[0:0]
is valid and will be 0-length,a[0:1]
will have a length of 1,a[0:len(a)]
valid and will have the same length asa
.In case of slices, the lower bound can even be greater than the slice length (but must not exceed the slice capacity). For more details, see Slicing: Out of bounds error in Go.
To add extra emphasis:
In case of slices, the lower bound can even be greater than the slice length (but must not exceed the slice capacity). For more details, see Slicing: Out of bounds error in Go.
and from that link:
Foreword: The question is good, the source of downvotes is because at first glance the question does seem like the asker simply doesn't know what the valid bounds are when slicing. But the case is uncommon! It is very rare that someone slices a slice in a way that even the lower bound is beyond the length of the slice!
In short: The problem is not with the lower bound which can be equal to or greater than
len()
(the upper limit is dictated bycap()
in case of slices). The problem is with the higher bound: it must be greater than or equal to the lower bound. And since you didn't specify the higher bound, it defaults tolen()
(and not tocap()
!) which is0
. And1
is not less than or equal to0
.For arrays or strings, the indices are in range if
0 <= low <= high <= len(a)
, otherwise they are out of range. For slices, the upper index bound is the slice capacitycap(a)
rather than the length.Since you are slicing a slice, indices are in range if:
0 <= low <= high <= cap(a)
So this line:
c := b[1:]
Is invalid, because:
A missing low index defaults to zero; a missing high index defaults to the length of the sliced operand.
So in your case
low = 1
andhigh = 0
(implicit), which does not satisfy:0 <= low <= high <= cap(a)
So for example the following expressions are valid:
c := b[1:1] // c len=0 cap=4 [] c := b[1:2] // c len=1 cap=4 [0] c := b[1:cap(b)] // c len=4 cap=4 [0 0 0 0]
This scratch code is based off of https://github.com/mactsouk/mastering-Go-3rd/blob/9329a87dd5afc3878831e3a98739fa1c8c361cd5/ch03/phoneBook.go and attempts to reason why the starting or lower bound can be equal to or greater than the length of the slice
Ultimately, I was attempting to understand why this works:
func deleteEntry(key string) error {
i, ok := index[key]
if !ok {
return fmt.Errorf("%s cannot be found!", key)
}
data = append(data[:i], data[i+1:]...)
// Update the index - key does not exist any more
delete(index, key)
err := saveCSVFile(CSVFILE)
if err != nil {
return err
}
return nil
}
more specifically:
data = append(data[:i], data[i+1:]...)
The goal is to retain the slice elements before/after the specific index value i
. This confused me if we're working with a slice like this one:
data := []string{
"first", // index 0
"second", // index 1
"last", // index 2
}
where the length of the slice is 3 (index positions 0, 1 & 2), but using a slice of expression of data[3:]
uses an index value one past the max index value of 2.
As noted in the last post to this issue, the lower bound index value can be equal to or greater than the value of len(data)
, provided it is lower than cap(data)
. This was (and still is) surprising, but I can see how the property is useful for slicing operations like this in order to exclude elements from a new slice.
As noted in the last post to this issue, the lower bound index value can be equal to or greater than the value of
len(data)
, provided it is lower thancap(data)
. This was (and still is) surprising, but I can see how the property is useful for slicing operations like this in order to exclude elements from a new slice.
As noted by others, you still have to guard against empty (and presumably nil
) slices.
This attempt to use range syntax on an empty slice:
package main
import (
"fmt"
)
func main() {
myarr := []int{}
for _, v := range myarr[1:] {
fmt.Println(v)
}
}
panics with this output:
panic: runtime error: slice bounds out of range [1:0]
goroutine 1 [running]:
main.main()
/tmp/sandbox3034326152/prog.go:10 +0x1a
Program exited: status 2.
One workaround mentioned (to safely skip first element in a slice):
for i := 1; i < len(Arr); i++ {
val = Arr[i]
// Do something
}
refs:
Working through the Slice Subsets portion of Cory LaNou's Getting Started with Go O'Reilly course and having some trouble wrapping my head around the slicing syntax.
The guide for the syntax was given as:
The code example:
Go Playground: https://play.golang.org/p/KqfpsxZzQIO