Skip to content

Array

In Go, an array is a fixed-size collection of elements of the same type. Arrays are declared with a specific size, and that size cannot be changed after the array is created. Arrays are useful when planning the detailed layout of memory and sometimes can help avoid allocation, but primarily they are a building block for slices. The size of an array is part of its type, so the types [10]int and [20]int are distinct. Arrays are equal if they are the same length and contain equal values.

Info

Arrays are rarely used directly in Go because of above reasons.

Declaration, Syntax and Use

run command
$ go run src/fundamentals/composite/arrays.go
Indexes not set get default value:  0
All values in the array:  [0 10 20]

Value stored in 0 index is 0
Value stored in 1 index is 10
Value stored in 2 index is 20

It's possible to change index value in array:  [0 600 20]

myArray == secondArray?  true
Other Declaration: [1 0 0 0 0 4 6 0 0 0 100 15]
package main

import "fmt"

func main() {
    var myArray [3]int
    myArray[1] = 10
    myArray[2] = 20
    fmt.Println("Indexes not set get default value: ", myArray[0])
    fmt.Println("All values in the array: ", myArray)
    fmt.Println()
    for i, value := range myArray {
        fmt.Printf("Value stored in %d index is %d\n", i, value)
    }

    fmt.Println()
    myArray[1] = 600
    fmt.Println("It's possible to change index value in array: ", myArray)
    fmt.Println()

    secondArray := [...]int{0, 600, 20}
    fmt.Println("myArray == secondArray? ", myArray == secondArray)

    var otherDeclaration = [12]int{1, 5: 4, 6, 10: 100, 15}
    fmt.Println("Other Declaration:", otherDeclaration)
}

As Function Params

A function can receive an array by specifying the array's type and size in the function's parameter list. When you pass an array to a function, Go passes the entire array by value, which means the function receives a copy of the array.

If you want to pass an array to a function and have the function modify the original array, you would need to pass a pointer to the array instead. However, arrays in Go are not addressable, so you cannot take the address of an array directly. Instead, you would typically use a slices, which is a reference to an underlying array and can be passed by reference.

Array Params Side-Effect

run command
$ go run src/fundamentals/composite/array_params.go
Inital myArray:  [1 2 3 4]
Memory allocated for myArray: 0xc000018180
Memory allocated for data: 0xc0000181e0
After call func Slice:  [1 2 3 4]
package main

import "fmt"

func arrayParam(data [4]int) {
    fmt.Printf("Memory allocated for data: %p\n", &data)
    data[0] = 90
}

func main() {
    myArray := [4]int{1, 2, 3, 4}
    fmt.Println("Inital myArray: ", myArray)
    fmt.Printf("Memory allocated for myArray: %p\n", &myArray)
    arrayParam(myArray)
    fmt.Println("After call func Slice: ", myArray)
}

Slicing an Array

In Go, slicing an array you create a new slices that references a portion of the original array. There are several ways to slice an array, depending on the range of indices you specify. The index format follows this pattern: a[start:stop].

Change the original array

Remember that slicing does not create a new array/slice. It creates a new slice header that points to the same underlying array. Both slices/arrays share the same memory, and changes to one are reflected in the other.. Avoid modifying slices after they have been sliced or if they were produced by slicing. Check out How to Work with Subslice.

Slicing Use Cases

run command
$ go run src/fundamentals/composite/arrays_slicing.go
To create a copy of the array, use full slice omit both the start and end indices:  [10 20 30 40 50 60 70 80 90 100]
Notice that the copyedArray is actually a slice variable: []int
Address of myArray: 0xc00007c000
Address of copyedArray: 0xc000010018
Check that changing the copyedArray changes also the myArray!!!!!!! [10 20 30 40 50 60 70 80 90 10000] [10 20 30 40 50 60 70 80 90 10000]

Getting 5 first elements, start to end:  [10 20 30 40 50]
Getting from index 5 to end, from to end:  [60 70 80 90 10000]
Getting between 2 and 5, between indexes:  [30 40 50]
Go does not have built-in support for stepping through a slice like Python does. However, you can achieve this by using a loop or by manually selecting the desired indices.
Stepped Slice:  [10 30 50 70 90]
package main

import (
    "fmt"
    "reflect"
)

func main() {
    var myArray [10]int

    for i := 0; i <= 9; i++ {
        value := (i + 1) * 10
        myArray[i] = value
    }

    copyedArray := myArray[:]
    fmt.Println("To create a copy of the array, use full slice omit both the start and end indices: ", copyedArray)
    fmt.Println("Notice that the copyedArray is actually a slice variable:", reflect.TypeOf(copyedArray))
    fmt.Printf("Address of myArray: %p\n", &myArray)
    fmt.Printf("Address of copyedArray: %p\n", &copyedArray)
    copyedArray[9] = 10000
    fmt.Println("Check that changing the copyedArray changes also the myArray!!!!!!!", copyedArray, myArray)
    fmt.Println()

    startToEnd := myArray[:5]
    fmt.Println("Getting 5 first elements, start to end: ", startToEnd)

    fromToEnd := myArray[5:]
    fmt.Println("Getting from index 5 to end, from to end: ", fromToEnd)

    betweenIndexes := myArray[2:5]
    fmt.Println("Getting between 2 and 5, between indexes: ", betweenIndexes)

    fmt.Println("Go does not have built-in support for stepping through a slice like Python does. However, you can achieve this by using a loop or by manually selecting the desired indices.")

    var steppedSlice []int
    for i := 0; i < len(myArray); i += 2 {
        steppedSlice = append(steppedSlice, myArray[i])
    }
    fmt.Println("Stepped Slice: ", steppedSlice)
}

How to Work with Subslice

To simplify slice handling, you should either never use append with a subslice or ensure that append operations do not overwrite existing data by employing a complete slice expression. This approach explicitly delineates the shared memory extent between the parent slice and the subslice. A complete slice expression comprises three parts, with the third part denoting the final position within the parent slice's capacity that remains accessible to the subslice. To calculate the subslice's capacity, subtract the starting offset from this value.

Tip

If you need to create a slice that’s independent of the original, , see Copying a Slice.

Complete Slice Expression

run command
$ go run src/fundamentals/composite/full_slice_expr.go
x: [a b c d] 5 4
y: [a b] 2 2
z: [c d] 2 2

x: [a b c d x] 5 5
y: [a b i j k] 5 5
z: [c d y] 4 3
package main

import "fmt"

func main() {
    x := make([]string, 0, 5)
    x = append(x, "a", "b", "c", "d")
    y := x[:2:2]
    z := x[2:4:4]
    fmt.Println("x:", x, cap(x), len(x))
    fmt.Println("y:", y, cap(y), len(y))
    fmt.Println("z:", z, cap(z), len(z))
    fmt.Println()
    y = append(y, "i", "j", "k")
    x = append(x, "x")
    z = append(z, "y")
    fmt.Println("x:", x, cap(x), len(x))
    fmt.Println("y:", y, cap(y), len(y))
    fmt.Println("z:", z, cap(z), len(z))
}

Convert Array -> Slice and Array <- Slice

If you have an array and you want to convert it to a slice, you don't actually need to do anything special. A slice is just a reference to an underlying array, along with a length. So, when you pass an array to a function expecting a slice, Go automatically converts the array to a slice that covers the entire array.

When you convert a slice to an array, the data in the slice is copied to new memory. Consequently, changes made to the slice do not impact the array, and vice versa. It's essential to specify the size of the array during compilation. While the array's size can be smaller than the slice's size, attempting to specify a larger size to will result in a runtime panic if the array's size exceeds the slice's length (not capacity).

Alternatively, you can utilize type conversion to transform a slice into a pointer to an array. Once a slice is converted to an array pointer, the storage is shared between the two. Any modifications made to one will reflect in the other.

Array to Slice

run command
$ go run src/fundamentals/composite/arrays_convert.go
printing s:  [1 2 3]
type of s:  []int
...
package main

import (
    "fmt"
    "reflect"
)

func printSlice(s []int) {
    fmt.Println("printing s: ", s)
    fmt.Println("type of s: ", reflect.TypeOf(s))
}

func panicArray(data []int) {
    panicArray := [5]int(data)
    fmt.Println(panicArray)
}

func main() {
    arr := [3]int{1, 2, 3}
    printSlice(arr[:])

    slice := []int{1, 2, 3}
    var arrCopy [3]int
    copy(arrCopy[:], slice)
    fmt.Println("Slice to Array", arr)

    slice = []int{1, 2, 3, 4}
    array := [4]int(slice)
    arraySmall := [2]int(slice)
    slice[0] = 10
    fmt.Println(slice)
    fmt.Println(array)
    fmt.Println(arraySmall)
    panicArray(slice)
}

Slice to Array

run command
$ go run src/fundamentals/composite/arrays_convert.go
...
Slice to Array [1 2 3]
[10 2 3 4]
[1 2 3 4]
[1 2]
panic: runtime error: cannot convert slice with length 4 to array or pointer to array with length 5
package main

import (
    "fmt"
    "reflect"
)

func printSlice(s []int) {
    fmt.Println("printing s: ", s)
    fmt.Println("type of s: ", reflect.TypeOf(s))
}

func panicArray(data []int) {
    panicArray := [5]int(data)
    fmt.Println(panicArray)
}

func main() {
    arr := [3]int{1, 2, 3}
    printSlice(arr[:])

    slice := []int{1, 2, 3}
    var arrCopy [3]int
    copy(arrCopy[:], slice)
    fmt.Println("Slice to Array", arr)

    slice = []int{1, 2, 3, 4}
    array := [4]int(slice)
    arraySmall := [2]int(slice)
    slice[0] = 10
    fmt.Println(slice)
    fmt.Println(array)
    fmt.Println(arraySmall)
    panicArray(slice)
}

References