Go panic: runtime error: index out of range but le

2019-02-26 02:51发布

问题:

i am having a hard time learning how to loop through a string in Golang to do some stuff(separate words than contain vowels).

I wrote this code snippet https://play.golang.org/p/zgDtOyq6qf Here is the error :

panic: runtime error: index out of range

goroutine 1 [running]:
panic(0x1045a0, 0x1040a010)
    /usr/local/go/src/runtime/panic.go:500 +0x720
main.myFunc(0x114130, 0x4, 0x0, 0x0, 0x0, 0x3ba3)
    /tmp/sandbox960520145/main.go:19 +0x1a0
main.main()
    /tmp/sandbox960520145/main.go:10 +0x40

I searched in this forum and someone said that it's due to the length of the array but it's not the case here. Ican't figure out how to solve this. Can someone please suggest anything? Thanks

回答1:

The issue is that you are creating a slice with length 0, but with a maximum capacity of 4, but at the same time you are trying to allocate already a value to the zeroth index of the slice created, which is normally empty. This is why you are receiving the index out of range error.

result := make([]string, 0, 4)
fmt.Println(len(result)) //panic: runtime error: index out of range

You can change this code with:

result := make([]string, 4)

which means the capacity will be the same length as the slice length.

fmt.Println(cap(result)) // 4
fmt.Println(len(result)) // 4

You can read about arrays, slices and maps here: https://blog.golang.org/go-slices-usage-and-internals



回答2:

First let's explain:

result := make([]string, 0, 4)

The make built-in function allocates and initializes an object of type []string call it Slice of string

Slice internals:

A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment).

So result := make([]string, 0, 4) allocates and initializes an object of type []string with length = 0 and capacity = 4.
And result := make([]string, 4, 4) allocates and initializes an object of type []string with length = 4 and capacity = 4, which is equal to result := make([]string, 4).

Now what is the difference between result := make([]string, 0, 4) and result := make([]string, 4):

With result := make([]string, 0, 4) the underlying array of this Slice is empty meaning using result[0] will panic: runtime error: index out of range.

With result := make([]string, 4) the underlying array of this Slice has 4 string elements, meaning using result[0], result[1], result[2], result[3] is OK:

package main

import "fmt"

func main() {
    result := make([]string, 4)
    fmt.Printf("%q, %q, %q, %q \n", result[0], result[1], result[2], result[3])
}

output:

"", "", "", "" 

And result := make([]string, 4) is equal to result := []string{"", "", "", ""} meaning this code:

package main

import "fmt"

func main() {
    result := []string{"", "", "", ""}
    fmt.Printf("%q, %q, %q, %q \n", result[0], result[1], result[2], result[3])
}

output is the same as above code:

"", "", "", "" 

The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated. Append returns the updated slice. It is therefore necessary to store the result of append, often in the variable holding the slice itself:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

As a special case, it is legal to append a string to a byte slice, like this:

slice = append([]byte("hello "), "world"...)

Now in your code inside function myFunc after result := make([]string, 0, 4), you may use append, like this working code (The Go Playground):

package main

import (
    "fmt"
    "strings"
)

func main() {
    strs := strings.Fields("Political srt")
    fmt.Println(len(strs)) // It's not empty so why index out of range
    fmt.Println(strs, strs[0], strs[1])
    fmt.Println(strings.ContainsAny(strs[0], "eaiuo"))
    fmt.Println(myFunc("Political srt"))
}

func myFunc(input string) []string {
    strs := strings.Fields(input)
    result := make([]string, 0, 4)
    for i := 0; i < len(strs); i++ {
        if strings.ContainsAny(strs[i], "eaiu") {
            result = append(result, strs[i])
        } else {
            result = append(result, strs[i])
        }
    }
    return result
}

You may simplify that code, like this working code (The Go Playground):

package main

import (
    "fmt"
    "strings"
)

func main() {
    fmt.Println(myFunc("Political srt"))
}

func myFunc(input string) []string {
    strs := strings.Fields(input)
    result := make([]string, 0, 4)
    for _, s := range strs {
        if strings.ContainsAny(s, "eaiu") {
            result = append(result, s)
        }
    }
    return result
}


回答3:

Index out of range error is appearing because you are not initializing the result array with sufficient length.

In myFunc, you have:

result := make([]string, 0, 4)

This creates a slice of strings which has an underlying array of length 4, but since you have declared slice length to be 0, none of the elements from underlying array are accessible to the slice. So even result[0] is out of range of the available indices.

To fix this, simply supply a sufficiently large length parameter to make:

result := make([]string, 4, 4)

You can read more about how slices operate here.