avatarSajal Dulal

Summary

The provided content offers an in-depth tutorial on slices, custom types, and receiver functions in Go, explaining their usage and benefits within the Go programming language.

Abstract

The article is part of the "Getting Started with GOlang" series and serves as a comprehensive guide to understanding slices, custom types, and receiver functions in Go. It begins by comparing arrays and slices, emphasizing the dynamic nature of slices and demonstrating how to create, modify, and manipulate them. The author illustrates how to initialize slices, append new elements, and slice existing ones. The article then transitions to custom types, explaining how to define new types and use them to create more readable and maintainable code. Finally, the concept of receiver functions (methods) is introduced, showcasing how they allow for the attachment of functions to custom types, similar to methods in object-oriented programming. The tutorial aims to solidify readers' understanding of these concepts through practical examples and encourages exploration and questioning to foster a deeper appreciation of Go's capabilities.

Opinions

  • The author believes that slices are a more versatile and practical data structure than arrays in Go due to their ability to grow and shrink.
  • Custom types are advocated for their role in improving code clarity and reducing confusion, especially when dealing with complex data structures.
  • Receiver functions are presented as a powerful feature in Go, enhancing the object-oriented nature of the language by allowing methods to be associated with custom types.
  • The article suggests that understanding these concepts is crucial for beginners to effectively work with Go and to take advantage of its capabilities.
  • The author encourages readers to experiment with the code examples provided and to engage with the content by asking questions for further clarification.

Slices, Custom Types and Receiver Functions(methods) in GOlang

This is the second article in the “Getting Started with GOlang” series. If you have not checked out the previous article please check it out before diving into this. 1. Getting started with GOlang 2. Slices, Custom Types and Receiver Functions(methods) in GOlang 3. Structs and Maps in GOlang 4. Pointers and Passby value/reference in GOlang 5. Interfaces in GOlang

ARRAY

Arrays in GO cannot shrink or grow, i.e completely static with predefined length. As we discussed in the previous article there are multiple ways of declaring variables and so is such for an array. You can either declare it first and provide values later or use the “:=to declare and assign values to it at the same time. Let’s see some examples.

var a [5]int
a[0] = 5
a[1] = 2

That is one way to initialize an array and then set the values as per needed, but it must not exceed the length 5, but since the indexing starts at 0, it goes from a[0] to a[4], making it a total of 5 values. Another way is:

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

Here, b is an array of length 5 and type int and all its values are set at the time of definition. You can print and check how these values are displayed.

SLICES

Now, slices are fancier arrays in GO which basically means there are special commands to, add additional values which make it grow, remove specific values to make it shrink, and a few more. Slices are initialized the same way arrays are, we just do not provide its length when initializing it.

Now, let's create a file in a new directory you could call it “slices” if you want. Then, create a new file main.go file and paste the following code.

package main
import "fmt"
func main() {
 cities := []string{"london", "paris", "newyork"}
 print(cities)
}
func print(c []string) {
 for i, city := range c {
  fmt.Println(i, city)
 }
}

What we did here is initially created a slice name “cities” with 3 string values. We also defined a function print, which takes a string-based slice as an input. We passed the city's slice to it. And then used the for range statement to loop across the slice and then used fmt.Println to print the index and the value.

Note: GO is a typed language so, whenever we pass arguments to a function we always need to state their type. This also goes for the return type.

We do not need to return anything in our function but here is a pattern to make you clear regarding return types.

func somefunctionname(c int, s float) string{
//your code here
return "something"
}

You can also return multiple values, just replace “string” with brackets and add the types of the values you are returning in order separated by “,” like:

func name(c int)(int, float, string){
//your code
return 5, 5.5, "five"
}

Now, let's go back to our program and run it with “go run main.go”. You should see something like:

0 london
1 paris
2 newyork

Now, add the following line after the city's initialization.

cities = append(cities, "amsterdam")

Now, run the program again. You should see something like:

0 london
1 paris
2 newyork
3 amsterdam

So, you realize a new value was added at the end of the slice. But, a few things need to be cleared. append does not add the value to the same slice as “push” does in javaScript. What append does is, take a slice and a value and then return a new slice with the given slice and the value at the end. We basically, set the new slice to the same variable making it seem like the same slice was updated. You can also append one slice of the same type into another one. You just need to add “…” after the second slice. If city1 and city2 are two string-based slices, then the command goes like

cities = append(city1, city2...)

Now let's get back to our code. after the “append ”line add the following line:

cities = cities[0:2]

Now, run the program, you should see something like.

0 london
1 paris

So the command does is splits the slice with the given indexes, but there is a small catch. Look at the following phrase, which should make it clear

cities[ startIndexIncluding : endIndexNotIncluding ]

So, when we used cities[0:2], “london ” which has an index of 0 was included but “NewYork ”which has an index of 2 was not included Also, there are a few other tricks: 1. If “statIndexIncluding” is not provided i.e we just use cities[:2], GO automatically understands it to be 0 or the first value. 2. if “endIndexNotIncluding” is not provided i.e we just use cities[2:], GO automatically understands it to be the last value.

Custom Type and Receiver Functions

Now, I hope you are getting the hang of how GO is working. This topic might seem a bit advanced for a beginner but GO is one of the most used things. So, create a new directory, create a new file main.go and add the following:

package main
import "fmt"
type cities []string
func main() {
Nepal := cities{"Kathmandu", "Pokhara", "Lumbini"}
Nepal.print()
}
func (c cities) print() {
 for i, city := range c {
  fmt.Println(i, city)
 }
}

Yeah, you can go ahead and run it using the same, “go run main.go” command. You should see the same kind of results.

0 Kathmandu
1 Pokhara
2 Lumbini

So, let me know what is going on step by step.

First, let's look at the line type cities []string What we did here is to define a new type of name cities which are of type []string. So now we can simply define a list of strings as cities without confusion and it is cleaner to use. Let's look at the first line of the main function.

Nepal := cities{"Kathmandu", "Pokhara", "Lumbini"}

What this does is initializes a variable “Nepal” of type cities which translates to a string-based slice, and adds the provided value to the slice.

Now let's take a look at the function below:

func (c cities) print() {
 for i, city := range c {
  fmt.Println(i, city)
 }
}

This looks a bit different than the previous function. These kinds of functions are called “Receiver Functions”. Note: This function is an example and doesn't have any argument or a return type but, a receiver function can have any number of argument or return types as a normal function would.

The only difference is in these functions a value and type are added in parenthesis before the function name is written. Now, In our case, there are (c cities). This suggests that these functions come attached to all the variables of type cities. These could be comparable to “methods” in JavaScript classes. And the “c” (again could be any name) is the value of the variable that is calling this function.

So if you look at the line:

Nepal.print()

Nepal ”is a variable of type cities, so it is able to call this receiver function attached to the cities type.

Now, inside the function print(), the “c” corresponds to the value “Nepal”. And hence, prints the results as we have seen. There could be any number of receiver functions attached to a type. These are very useful for binding functions which we specifically need for only one type of variable. It is also more convenient to use.

Hope. This you find this useful. I will be writing further articles on GO to widen your curiosity. Feel free to question anything that I missed or misunderstood. Happy Coding :)

Golang
Beginner
Receiver Functions
Slices
Custom Types
Recommended from ReadMedium