Go “panic: runtime error: index out of range” when the length of array is not null

jiji picture jiji · Aug 24, 2016 · Viewed 26.5k times · Source

I am having a hard time learning how to loop through a string in Go to do some stuff (specifically, to separate words than contain vowels).

I wrote this code snippet: https://play.golang.org/p/zgDtOyq6qf.

Here is the error I’m getting when running it:

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. I cannot figure out how to solve this issue. Can someone please suggest something?

Answer

user6169399 picture user6169399 · Aug 24, 2016

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
}