Slice out of bounds using [][]int but works with map[int][]int

249 Views Asked by At

Why does this code work

graph := make(map[int][]int, 0)

graph[0] = append(graph[0], 1)

But if you replace first line with graph := make([][]int, 0) I get panic: runtime error: index out of range? It's very weird.

4

There are 4 best solutions below

0
On BEST ANSWER

When you do make in graph := make(map[int][]int, 0), you are allocating memory to your map, not to array. So you might do this only graph := make(map[int][]int).

Decomping you code:

type a []int
type m map[int]a

func main() {
    fmt.Println("Hello, playground")

    //decomping graph := make(map[int][]int, 0)
    graph := make(m)

    //map is empty
    fmt.Println(len(graph))

    //decomping graph[0] := append(graph[0], 1)
    itm_a := 1
    arr_a := []int{}

    //appeding item to "a" type
    arr_a = append(arr_a, itm_a)

    //appending array of a to graph
    graph[0] = arr_a

    //print graph
    fmt.Println(graph)
}

See in playground.

The error that you are getting is caused for conceptual error. When you does graph := make([][]int, 0), you are allocation memory to a slice of slice, not an array. See in https://blog.golang.org/go-slices-usage-and-internals.

So you can does this (decomping solution):

type a []int
type m []a

func main() {
    fmt.Println("Hello, playground")

    //decomping graph := make([][]int, 0)
    //see that you must be set the length
    graph := make(m, 0)

    //map is empty
    fmt.Println(len(graph))

    //this is incorrect: graph[0] := append(graph[0], 1)
    //this is correct:   graph[0] := append(graph[0], []int{1})
    //see:
    itm_a := 1
    arr_a := []int{}

    //appeding item to "a" type
    arr_a = append(arr_a, itm_a)

    //appending slice of a to graph (slice)
    graph = append(graph, arr_a)

    //print graph
    fmt.Println(graph)
}

See in playground

0
On

The code panics due to slice length being 0. If you want to append anything to the slice, you just have to provide its length as below.

graph := make([][]int, 1)
fmt.Println(len(graph))
graph[0] = append(graph[0], 1)
fmt.Println(graph)

To append data to a slice at first level, append to its first index and then append to second level just like below.

graph := make([][]int, 0)
fmt.Println(len(graph))
graph = append(graph, []int{1})

Check Playground example

0
On

Lets simplify your code, to make it more obvious what's happening (Playground link):

graph1 := make(map[int]int, 0)
graph2 := make([]int, 0)

x := graph1[0] // Success
y := graph2[0] // Panic

From this we see that the difference is due to map[int] vs []int -- the second array in your type is actually irrelevant.

Now to understand why this is happening, we need to understand how Go handles map and slice access. From Go Maps in Action we learn:

If the requested key doesn't exist, we get the value type's zero value.

In your original code, the zero value for a slice ([]int), is nil, and append() treats nil as the first argument as an empty slice.

But when we try to access the 0th element of an empty slice, we get a panic, because the slice is empty.

In summary, append and the second slice of your type are both red herrings in your question. The panic happens when trying to access the non-existent element in the first dimension of your slice.

0
On

make(map[int][]int, 0) creates a map of []int.

By design in Go you can get any element from a map. And if it doesn't exist you receive the "zero" value which here is an empty slice.

graph := make(map[int][]int)

graph[4] = append(graph[4], 1)
graph[7] = append([]int{}, 1, 2)
graph[11] = append([]int{1, 2, 3}, 4, 5)

printing it gives this slice:

fmt.Printf("%#v\n", graph)

map[int][]int{
    4:[]int{1},
    7:[]int{1, 2},
    11:[]int{1, 2, 3, 4, 5},
}

Your second example create an empty slice of []int slices. Slices work differently from maps, so indexing an element that doesn't exist will give you a panic.