Slices and Arrays in Go language

We will take a closer look at arrays and slices in Go and try to understand their internals and fundamentals. We will also take a look at different behaviour when passing array to a function vs slice to a function. Let’s Go !

Arrays in Go:

Array is a data structure whose memory is allocated sequentially. Memory in sequential form lets memory in use stay loaded into CPU caches for a longer period of time. Using index arithmetic, we can loop through all of the items. Because the length of an array is determined by its type, arrays cannot be resized.

Declaring and Initializing slices

In Go, we can declare an array by providing its data type and length.

var array[5] int

Array can also be declared and initialized on same line.

array := [5] int {1,2,3,4,5}

We can also assign values to specific indexes

/*Here index 1 and index 2 are assigned values 10 and 20 respectively while other indexes are set to default value 0*/array:= [5]int {1:10,2:20}

Slices in Go:

Slices are tiny objects that abstract and manipulate an underlying array. They’re adaptable in terms of growth and include a built-in function called append that allows us to efficiently increase slices. Its also possible to reduce the size of slice by slicing out a part of underlying memory. Slices are far more prevalent in practice than arrays.

Declaring and Initializing slices

Inbuilt function make allows us to declare a slice with a data type, length and capacity. We can also declare it using a slice literal.

//Creates slice of string with length and capacity of 5
slice := make([]string,5)
//Creates slice of string with length of 3 and capacity of 5
slice2 := make([]string,3,5)
//Slice literal declaration of length and capacity of 3
slice3 := []string{"a","b","c"}

We can slice an existing slice to create a new slice.

slice := []string{"a","b","c"}//slice2 will be [b,c]
slice2 := slice[1:3]

Note that now these two slices have same underlying array. Changes made to shared section of underlying array by one slice will be reflected to the other slice as well.

There is also a three index slice which allows us to restrict the capacity.

//Creates slice with length 1 and capacity of 2 
slice := array[2:3:4]

Here, the way length and capacity are calculated for a slice[i:j:k] is
Length = j-1
Capacity = k-i

The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s).

Appending to a slice

Append accepts a source slice and a value to be added and returns a new slice with the modifications. The append function will always increase the size of slice as needed. Capacity may or may not be affected depending on available capacity of source slice.

s :=[]int{1,2,3,4,5}
s2:= s[1:3]
s2:= append(s2,6)

In this case, because there was available capacity in the underlying array for s2, the append operation incorporated the available element into the s2 length and assigned the value. Because the original slice shares the underlying array, the changes in index 3 are also visible to s.

But what if there isn’t enough capacity?

//Creates a slice of int with length and capacity of 4
slice := []int{1,2,3,4,5}
//Appends 6 to slice and assigns the new slice to slice2
slice2 := append(slice,6)

Following this append action, slice2 receives its own underlying array, and the array’s capacity is doubled from its initial size.

Passing array between functions :

Passing an array between functions may be a memory and performance consuming process. When we provide an array to a function, it is copied and sent to the function regardless of its size, which is known as passing by value.
The larger the array, the more costly this becomes.

A way around

We may avoid passing arrays by value between functions by passing arrays by pointer.

var array[5] int//Function foo accepts a pointer to array of 5 integers
foo(&array)
//Function foo accepts a pointer to array of 5 integers
func foo(array *[5]int){
//Function body
}

This operation is much more efficient with memory and performance. Note that since we are using pointer, changing value that pointer points to will change the value everywhere else in the program since the memory is shared.

Better solution:

There’s a better solution than passing arrays by pointer. Its by using and passing slices !

Passing slices between functions :

slice := make([]int,10000)
foo(slice)
//Function expects a slice of integer
func foo(slice []int){
//Function body
}

In this scenario, only the slice is copied and passed by value and not the underlying array which is much more efficient than passing arrays. This is why slices are so convenient. You won’t have to deal with complex syntax or pass pointers around. You just duplicate your slices and make the necessary modifications.

Iterating over slices and arrays

Like most of the other languages we can iterate over slices and arrays using our trusty for loop

slice:= []int{1,2,3,4,5}
for index:=2 ;l index < len(array); index++{
fmt.Printf("Value %d at Index %d",slice[index],index)
}

The len function returns the length of an array or slice. The same for loop applies to arrays as well. But, there is a Go way to do it.

slice := []int{1,2,3,4,5}array := [5]int{1,2,3,4,5}//iterating over slice
for index,value := range slice{
fmt.Println(β€œValue: %d\n with its Index : %d”,value,index)
}
//iterating over array
for index, element := range array{
fmt.Println(index, "=>", element)
}
//ignoring index by using black identifier
for _, element := range array{
fmt.Println(element)
}

The range keyword returns two values. First is index and second is the copy of value in that index position.

That’s all for arrays and slices for now. Thank you for taking the time to read this. If you enjoyed this post, share it with a friend! Until next time🌸

References:

--

--

π”Έπ•’π•€π•™π•šπ•€π•™ π•π•šπ•”π•™π•’π•£π•–
Geek Culture

My name is Ash. I don’t own a Pikachu yet, but my love for coding and creating isn’t shocking at all.