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

后端 未结 3 1337
孤街浪徒
孤街浪徒 2021-01-03 03:37

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:

相关标签:
3条回答
  • 2021-01-03 04:22

    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

    0 讨论(0)
  • 2021-01-03 04:26

    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
    }
    
    0 讨论(0)
  • 2021-01-03 04:36

    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.

    0 讨论(0)
提交回复
热议问题