Background
Quora helped me discover Golang back in the late 2015. Go was instant hit in my mind because the multi-threading code in Go was very simple and easy to understand compared to that of C++ 11. I tried to set it up and I failed multiple times but finally I was successful and I tried it a few times and left it to complete the C++ understanding. I am trying it again since the last few months and I will share about the essential topics like Slices, Pointers, Maps, Interfaces, Goroutines and the Context package. Let’s get started.
I assume you already know the history of Go (which is worth knowing) and hence I will directly jump to the topic.
Arrays
Almost all the programming language supports an in-built data structure called array. Arrays are basically a contagious allocated block of memory which allows storing a fixed size of elements into it.
Golang also supports arrays and we can create one like:
1 | package main |
Problem with Arrays
While arrays are helpful for string a collection there are a few caveats of using static arrays:
- They are of fixed size and we have to provide the size while creating the arrays.
- In golang, arrays are value type which means it’s copy by value and this impacts performance for large sized arrays.
- In golang syntax of array can cause confusion with that of arrays and since golang promotes use of slices.
Slices
In golang, slices are actually like vector in C++. It is a resizable, referential value. This means slices can automatically expand at runtime and also it is not copied while passing to functions (which makes it performance friendly).
Let’s have a look at slices:
1 | package main |
Capacity and Length
Slices are dynamic in nature, and it has 2 properties capacity and length. The length of the slice increases on pushing items to it and decreases when we move items out of slice. The capacity is the total length of the underlying array.
We use len() function to get the length og the slice and the cap() function to get the capacity of the slice.
We can specify the capacity of the slice if we create it using make() function. And when the size of the slice is about to be exhausted, golang will create and array of double size and transfer the elements to the new array and delete the old array.
Let’s look at an example:
1 | package main |
When we will run it we will see the output like:
1 | 3 3 |
When the code executes, the slice t is created using make([]string, 3).
This allocates an underlying array of size three and produces a slice whose length and capacity are both set to three. Since no extra capacity was reserved, the slice has no room to grow without reallocation. Therefore, the first printed values are 3 3.
The next stage occurs when "a" and "b" are appended. The slice currently has a length of three and a capacity of three, meaning it is completely full. Appending any new element forces Go to allocate a new, larger underlying array. To make future appends efficient, Go does not simply increase the capacity by the number of appended elements, for smaller slices, it typically doubles the capacity.
That’s how the flow goes on 3 3 then 5 6 and finally 7 12.
Expanding Slices
We already seen above that we can use the function append(slice s, elements e1, e2, e3....) to add new elements to the slice. The append function returns a new slice everytime and the internal mechanism is also explained above.
We can append elements and even slices to an existing slice.
1 | package main |
The output will be [H e l l o W o r l d]. We have appended the slice w to t making it a larger slice.
Iterations
To loop over the elements of the slice we can use the range function in Go.
1 | package main |
This will print Hello in the console.
Copying
We can copy one slice to another using the copy(dest, src) function. If we are not aware of the size then we can use len() function with make() to copy a slice.
1 | package main |
Re-Slicing
This is one of the important concept related to Slices in Go!.
Reslicing in Go refers to creating a new slice by adjusting the boundaries of an existing one, using the slice expression syntax s[a:b] or the full form s[a:b:c]. Reslicing allows us to shrink or expand a slice within its capacity
When we reslice, we are not creating new data; instead, we are creating a new window into the same underlying array. This means the new slice shares memory with the original, and changes to one will reflect in the other as long as they overlap in the same underlying array.
1 | package main |
Clearing a Slice
We can clear the slice without impaling the capacity of the lice by using this syntax s = s[:0] and to release the memory we can use s = nil.
Outro
I hope you enjoyed reading this, stay tuned for the next topic - Pointers in Go.