Difference b/w Slice and Array Golang
This is a very basic but frequently asked question in Golang interviews. I have observed that many people fail to answer this correctly. I have seen many people using these in their code without actually understanding how they work behind the camera.
Arrays
Arrays is a collection of fixed number of same type of elements. While creating an array, size is always the part of type itself. Just like you cannot assign a string value to integer value, you cannot use arrays of different sizes interchangeably.
func main() {
var arr1 = [4]int{1,2,3,4}
var arr2 [6]int
arr2 = arr1
fmt.Println(arr2)
}
Output
./prog.go:12:7: cannot use arr1 (type [4]int) as type [6]int in assignment
In the above code, I first created an array arr1 of type [4]int. Then I created another array arr2 of type [6]int. These two are different types because sizes are different so I cannot assign arr1 to arr2. Hence the error. Arrays are fixed in length, and its length cannot be changed at runtime. Below code snippet will throw an error
func main() {
var arr [10]int
fmt.Println(arr)
arr = append(arr , 1)
fmt.Println(arr)
}
Output
./prog.go:10:14: first argument to append must be slice; have [10]int
Only use case of arrays in real programs is when you know exactly the size of your array. Otherwise slices are used most of the time which are more convenient to use.
Slices
Slices are just a wrapper around arrays. They help you do some operations on top of arrays. When I say wrapper, I mean that they do not hold any value itself, they reference an underlying array on which they perform operations.
While creating a slice, you do not specify the size. This is the key difference between arrays and slices.
var s []int
Length and Capacity of a Slice
Slices can also be created using built-in make function
func make([]T, len, cap) []T
Here T is type of elements, length is for current count of elements and capacity is maximum elements it can hold. When this function is called, it creates an array and returns a slice which refer this array. Slice holds three values , a pointer to first element of array, length and capacity. Capacity field is optional, when not specified its picks the value of length.
func main() {
arr := make([]int, 5, 10)
fmt.Printf("Array: %v, capacity: %d, length: %d", arr, cap(arr), len(arr))
}
Output
Array: [0 0 0 0 0], capacity: 10, length: 5
What happens when we try to add element to slice when its capacity is full
func main() {
arr := make([]int, 5, 6)
fmt.Printf("Array: %d, Address: %p, capacity: %d, length: %d \n", arr, &arr[0], cap(arr), len(arr))
arr = append(arr, 1)
fmt.Printf("Array: %d, Address: %p, capacity: %d, length: %d \n", arr, &arr[0], cap(arr), len(arr))
arr = append(arr, 1)
fmt.Printf("Array: %d, Address: %p, capacity: %d, length: %d \n", arr, &arr[0], cap(arr), len(arr))
}
Output
Array: [0 0 0 0 0], Address: 0xc0000b2030, capacity: 6, length: 5
Array: [0 0 0 0 0 1], Address: 0xc0000b2030, capacity: 6, length: 6
Array: [0 0 0 0 0 1 1], Address: 0xc000094060, capacity: 12, length: 7
First I created a slice with length of 5 and capacity of 6. It means that one more element can be added to underlying array. We can see the address of underlying array by printing the address of its first element. When I appended one element, it added that to the same underlying array. Next, when I tried to append one more element, at that point its capacity was full so it created another array and copied all the values from previous array into it and doubled its capacity and started referencing to it instead. You can see the change in address of underlying array.
Creating a slice from an existing array
You can create a slice from an existing array using : descriptor.
func main() {
arr := [5]int{1,2,3,4,5}
s := arr[:]
fmt.Println(arr)
fmt.Println(s)
}
Output:
[1 2 3 4 5]
[1 2 3 4 5]
You can also select a part of an array instead by providing the start and end index. If you don't provide any indices it will reference the entire array. Index provided before the colon is inclusive but index provided after the column is exclusive. It means if you say arr[1:4], it will pick from index 1 up to index 3. See below snippet for examples
func main() {
arr := [5]int{1,2,3,4,5}
s1 := arr[:] // all elements
fmt.Println(s1)
s2 := arr[2:4] // starting index 2 , upto index 3
fmt.Println(s2)
s3 := arr[2:] // starting index2 , upto last element
fmt.Println(s3)
s4:= arr[:4] // starting index 0, upto index 3
fmt.Println(s4)
}
Output
[1 2 3 4 5]
[3 4]
[3 4 5]
[1 2 3 4]
If you want to get more deeper understanding of these concepts and internal workings, Please head out to below links.