Arrays vs Slices

Working with GO, one is tempted to think that arrays and slices are the same things other than array having an immutable size. That's not true and some quirks should be known. Take the following function for example (for readability no checks are done on slice bounds):

type NamesStruct struct {
    NamesArray [3]string
    NamesSlice []string
}

func print(a NamesStruct) {
    a.NamesArray[2] = "Massimiliano"
    a.NamesSlice[2] = "Massimiliano"
    fmt.Println("Printing Values inside called function:")
    for i, _ := range a.NamesArray {
        fmt.Printf("NamesArray[%d]=%s\n", i, a.NamesArray[i])
        fmt.Printf("NamesSlice[%d]=%s\n", i, a.NamesSlice[i])
    }
}

The print function takes a struct containing an array (NamesArray) and a slice (NamesSlice) as parameters, changes the value of the second element of both and then prints their content.

Invoking it with a code like this:

a := NamesStruct{
        NamesArray: [3]string{"Max", "Massimo", "Maximilian"},
        NamesSlice: []string{"Max", "Massimo", "Maximilian"},
    }
print(a)

fmt.Println("Printing Values inside caller function:")
for i, _ := range a.NamesArray {
    fmt.Printf("NamesArray[%d]=%s\n", i, a.NamesArray[i])
    fmt.Printf("NamesSlice[%d]=%s\n", i, a.NamesSlice[i])
}

we would expect the slice and the array to contain identical values both inside the caller function and in the called one. That's not the case. The output will be:

Printing Values inside called function:
NamesArray[0]=Max
NamesSlice[0]=Max
NamesArray[1]=Massimo
NamesSlice[1]=Massimo
NamesArray[2]=Massimiliano
NamesSlice[2]=Massimiliano
Printing Values inside caller function:
NamesArray[0]=Max
NamesSlice[0]=Max
NamesArray[1]=Massimo
NamesSlice[1]=Massimo
NamesArray[2]=Maximilian
NamesSlice[2]=Massimiliano

As you can see, the slice reflects the changes done inside the called function, but the array doesn't. Why? The reason is that Go passes EVERYTHING by value, but since the slice is basically a header pointing to a backing array, just that header is passed by value (thus pointing to the same backing array). The array, on the other hand, is copied and, thus, won't reflect the change.

Complete example code

package main

import "fmt"

type NamesStruct struct {
    NamesArray [3]string
    NamesSlice []string
}

func print(a NamesStruct) {
    a.NamesArray[2] = "Massimiliano"
    a.NamesSlice[2] = "Massimiliano"
    fmt.Println("Printing Values from called function:")
    for i, _ := range a.NamesArray {
        fmt.Printf("NamesArray[%d]=%s\n", i, a.NamesArray[i])
        fmt.Printf("NamesSlice[%d]=%s\n", i, a.NamesSlice[i])
    }
}

func main() {
    a := NamesStruct{
        NamesArray: [3]string{"Max", "Massimo", "Maximilian"},
        NamesSlice: []string{"Max", "Massimo", "Maximilian"},
    }
    print(a)

    fmt.Println("Printing Values from caller function:")
    for i, _ := range a.NamesArray {
        fmt.Printf("NamesArray[%d]=%s\n", i, a.NamesArray[i])
        fmt.Printf("NamesSlice[%d]=%s\n", i, a.NamesSlice[i])
    }
}