"Go Programming - basics"

Published: Mon 21 June 2021

In content.

Go Programming - basics

golang Installation - Linux

This guide/article was written for Debian 10 GNU/Linux running latest updates. Before you begin, run following command to update your system.

  • #apt update && apt upgrade -y

Once you have completed above step, now it’s time to install Go Programming Language or golang in your Linux host.

First, you need to install a program called curl to fetch the packages files for golang.

  • apt install -y curl

Once you have installed curl, head over to official Go downloads page) and select appropriate golang binary release’s tarball or package.

Now download the package using curl command.

  • $curl https://dl.google.com/go/go1.14.3.linux-amd64.tar.gz --output go1.14.3.linux-amd64.tar.gz
    Note: You can check the downloaded package’s hashsum if you are unsure about the package you have downloaded.

Now you need to extract the contents of the golang installation package you have downloaded.

  • $tar xzvf go<VERSION>.linux-amd64.tar.gz
    You will have a directory called /go in your home directory.

You will need to change the permission of the directory and it’s content to user and group root

  • $sudo chown root:root ./go

Move the file to /usr/local directory. This is done to ensure the files in go directory is available throughout the environment session.

  • $sudo mv go /usr/local
    If you get permission errors, try following command:
  • #tar -C /usr/local -xvzf go1.14.3.linux-amd64.tar.gz
    This will extract the contents of the package into /usr/local directory

Now, create appropriate golang work directory in your home directory.

Edit ~/.profile from non-root user and insert the following lines:

  • export GOPATH=$HOME/<GO_WORKDIR>
  • export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
    Refresh your profile by running:
  • $source ~/.profile

Testing your installation

Now, go into the directory you have created for your golang work, and create a directory hierarchy folder by running following command:

  • $mkdir -p <GOLANG_WORKDIR>/src/my-1st-project/hello
    Once you have done that, you can create hello.go file inside above directory. Open the hello.go file using your favorite text editor and enter following code:
package main

import "fmt"

func main() {
   fmt.Printf("Hello, World!\n")
}

Once you have done that, you need to compile the code by invoking go install command.

  • $go install my-1st-project/hello

Now you can run the compiled program by calling hello in your current directory. It should print out Hello World.

Anatomy of a Go program

// this program imports fmt package and print
// "Hello, World" to the output
package main

import (
    "fmt"
)

func main() {
    fmt.Printf("Hello, World\n")
}
/*
This program imports fmt package and print
"Hello, World" to output
*/
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, World")
}

In above code, first few lines will be denoted for comments about the code. You can start writing comments by adding // (double forward-slash) or by /* (forward-slash and asterisk) and closing with */ (asterisk and forward-slash).

The package package main has a special meaning in Go and it will make your code compile for an executable and not the package

Next , import ( "fmt" ) imports fmt package which does formatted printing.

Finally, its our main program code. It is started with func main() then followed by {}. In the {}, we will write a function that is provided by fmt package, that is Println or Printf followed by the content inside ().

Go tools

Golang comes with a binary called go. It is used for tasks such as building, testing, benchmarking, and starting third party packages and more.

Copy below code into welcome.go.

// Print a friendly greeting

package main

import (
        "fmt"
)

func main() {
        fmt.Println("Welcome Gophers ☺")
}

Now, type following command to run the file.

  • $go run welcome.go

You should see a statement printed out with smiley face. What happens in the background is that go interpretes the code and print the ouput.

Now, type the following command to compile the code.

  • $go build welcome.go
    This will compile welcome.go file into an executable depending on your host architecture.

Go Basics

Let’s write a program that calculates the mean or average of two numbers. Following code does it.

// calculate mean of two numbers
package main

import (
"fmt"
)

func  main() {
var  x  int
var  y  int

x = 1
y = 2

fmt.Printf("x=%v, type of %T\n", x, x)
fmt.Printf("x=%v, type of %T\n", y, y)

var  mean  int

mean = (x + y) / 2

fmt.Printf("result: %v, type of %T\n", mean, mean)

}

In above code, you declare two variables called x and y with int as its variable type. By default golang will assign value 0 to the variable until a value is assigned to it which is done in the next line, x=1 & y=1. Following line prints the value and the type, %v denotes value and %T denotes the type. Then we declare a variable with type int called mean and perform calculation for mean in the following line. Finally we print the result and it’s type.

To run the code, simply type go run <FILENAME>.go. You should see some output. Lets examine the output.

In the output, you would have noticed that the result of mean operation is 1 when the mathematical result is 1.5. To solve this, we will introduce float64 variable type. There is also float32. Following code snippet will show you.

var x float64
var y float64

x = 1.0
y = 2.0

You can shorten the code above by writing the following:

var x, y float64
x, y = 1.0, 2.0

Which can be simplified by writing following:

x, y := 1.0, 2.0

This means that the variable x and y is created and assigned with floating point value. Golang will automatically detect it’s variable type in runtime or during compilation.

If and Switch

if..else Statement

package main

import  "fmt"

func  main() {
x := 10

if x > 5 {
    fmt.Println("X is large")
}

}

In above code, we are checking if value x is bigger than 5. Unlike Java or C, you do not need parentheses to express the condition. Now the condition is met, it will print X is large.

Lets write a if..else statement. Following code shows the usage of if..else:

if x > 100 { fmt.Println("X is big") }
else { fmt.Println("X is small") }

Now if you declare a variable x and assign a value that is lesser than 100, the program will execute the else statement block because it did not satisfy the condition. Next, I will introduce else if statement.

x := 20

if x < 10 {
    fmt.Println("X is lesser than 10")
} else if x < 30 {
    fmt.Println("X is lesser than 30")
} else {
    fmt.Println("No match")
}

In above code, we have introduced else if. Suppose you have multiple if conditions in which one of the condition is fulfilled, the statement block for that else if statement will execute. So in this case, variable x has a value of 20. So when the code is interpreted, it will match the second if statement, else if because x is lesser than 30.

switch Statement

package main

import (
    "fmt"
)

func main() {
    x :=2

    switch x {
        case 1:
            fmt.Println("1")
        case 2:
            fmt.Println("2")
        case 3:
            fmt.Println("3")
        default:
            fmt.Println("Default")
    }
}

Above code is how you write a switch statement in golang. You declare switch by giving it a parameter x and declare case 1..3 and case default when none of the cases match. When you run the code by typing:
-$go run <FILENAME>.go
You will see that the program outputs 2 as the value stored in parameter x matches case 2. Now if you do not want to match any case, match the default case, change the value of x to zero as example snippet below:

x := 0

switch x {
    case 1:
        fmt.Println("1")
    case 2:
        fmt.Println("2")
    default:
        fmt.Println("Default")
}

When you run above code, complete with package, imports and main function, you will observe that it outputs to default as value x does not match any of the case in the switch statement. Switch statement also allows string to be used in case statement. Following code shows an example:

x := "case"

switch x {
    case "case":
        fmt.Println("match")
    case "different":
        fmt.Println("different match")
    default:
        fmt.Println("default")
}

As with other switch case statements, this code block checks if the variable x is match in the case statements. It should print out match as it matches case case.

You can also do the following:

x := 2

switch {
    case x < 10:
        fmt.Println("more than 10")
    case x > 100:
        fmt.Println("more than 100")
    default:
        fmt.Println("default")
}

In above case, you can directly assign condition to the case statement. In this code, it will execute the first case statement as it matches with the condition provided.

For loops

package main

import (
    "fmt"
)

func main() {
    for x := 0; x < 3; x++ {
        fmt.Println(x)
    }
}

Above code is one example on how you can write for loop statement. The statement starts with variable x which you can change, then the condition, which is x<3, and then the increment x++. So the code start from 0, check if 0 is lesser than 3, print the value of 0 and then increase the value of 0 by 1. Upon next execution, the same step happens, until the condition is fulfilled.

Another example:

for x := 0; x < 3; x++ {
    if x > 1 {
        break
    }
    fmt.Println(x)
}

In above code, the for loop runs until it satisfy the if condition inside the for loop. Once it satisfies the condition, it breaks from the loop. Therefore only value 0 and 1 will be printed.

In the next example we will look how we can use continue to avoid printing 0 and print 1 and 2 instead.

for x := 0; x < 3; x++ {
    if x < 1 {
        continue
    }
    fmt.Println(x)
}

In this example, when the for loop runs, it checks the if condition, since it satisfies the condition it continues the execution without printing the value of x.

You can also write for condition in following way:

x := 0
for x < 3 {
    fmt.Println(x)
    x++
}

This is like while loop in other languages.

In final example, for loop can also be written in following way:

x := 0

for {
    if x > 2 {
        break
    }
    fmt.Println(x)
    x++
}

In above example, the for loop runs and checks the condition of if statement, since it does not pass the condition, it prints out the value of x and increment the value of x. When it satisfies the if condition, it simply breaks from executing.

Strings

package main

import (
    "fmt"
)

func main() {
    string := "This is a string"
    fmt.Println(string)
    fmt.Println(len(string)
    fmt.Printf("string[0] = %v (Type %T)", string[0], string[0])
}

In above code, we declare a variable string with the value. In the next line we simply print the content of the variable. In following line, we print the length of the string. Finally we check the first index of the string variable. print it’s value and it’s type which will be uint8 is a byte.

If you try to assign a new value to the first index, as shown below:

string[0] = 11

The go compiler will throw an error saying you cannot assign the value. This tells us that strings are immutable, that is once a string is assigned to a variable it cannot be changed.

Following code will concatenate two strings together:

fName := "Vigneshwaran"
lName := "Ravichandran"
fmt.Println(fName + " " + lName)

Unicode

Strings in golang support unicode characters. Following example shows unicode support:

fmt.Println("This is ½")

Multiline

Following code shows how you can write multiple lines in golang.

string := `
        This is a string.
        This is a second string.
        `
fmt.Println(string)

This will output in a multiple line output.

Slice

To access parts of the string you can use slice. Example below shows how slicing works.

string := "This is a string"
fmt.Println(string[4:11])
fmt.Println(string[4:])
fmt.Println(string[:4])

In above code snippet, we have assigned a string to a variable and in 2nd line we slice the string from index 4 to index 10 omitting index 11. In following line, we I have specified the start index but left end index empty. This will result in the printing to start from index 4 to the end of the index. In last line we have specified the end index. So the printing will start from the start of index until the 3rd index omitting 4th index.

Slice

package main

import (
"fmt"
)

func  main() {
members := []string{"Vigneshwaran", "Uthayamurthy", "Letchumi"}
fmt.Printf("members = %v (Type %T)\n", members, members)
fmt.Println(len(members))
fmt.Println(members[0])
fmt.Println(members[1:])
}

A slice a sequence of items. All items in the slice must be same type. After func main() , we specify the type by square brackets then the type string. Then we will the slice with curly brackets. You can use the len function to measure the length of the slice. You can also start from specific position or specify specific position of the slice. The 2nd line after slice declaration will print out the values in the slice and it’s type.

To access the elements in the slice, you can do the following:

for  x := 0; x < len(members); x++ {
fmt.Printf("Member: %v\n", members[x])
}

x := 0

for x < len(members) {
if members[x] == "Uthayamurthy" {
fmt.Printf("Member found. Member Name: %v\n", members[x])
}
x++
}

for  x := range members {
fmt.Println(x)
}

for  x := range members {
fmt.Printf("Member Name: %s\n", members[x])
}

for  index, name := range members {
fmt.Printf("Index: %d = Name: %s\n", index, name)
}

for  _, name := range members {
fmt.Printf("Member Name: %s\n", name)
}

In above code snippet, you can you traditional for loop to access the slice. member[x] will access the value in the index. You can also use range. Using range, you will need to specify the index and the value variable you want. Then you can access the member of the slice. To omit the index, in golang you can use underscore _.

To append an element into the slice, we use append. Following example shows the usage of append:

pets := []string{"Dog","Cat"}

pets = append(pets, "Bird")

for _, pet := range pets {
    fmt.Println(pet)
}

The output of above code will include element Bird in the slice.

Maps

credentials := map[string]string{
"username": "vgnshlvnz",
"password": "!@m@h@ck3r",
}

fmt.Println(len(credentials))

fmt.Printf("Username: %s\n", credentials["username"])

fmt.Printf("Password: %s\n", credentials["password"])

for  _, value := range credentials {
fmt.Printf("Value: %s\n", value)
}

for  key, value := range credentials {
fmt.Printf("%s = %s\n", key, value)
}

credentials["url"] = "https://facebook.com"

for  key, value := range credentials {
fmt.Printf("%s = %s\n", key, value)
}

delete(credentials, "url")

for  key, value := range credentials {
fmt.Printf("%s = %s\n", key, value)
}

In above code snippet, you declare a map by typing out map followed by the type of the key and the type of the value. In it, you declare your key, value pairs with a comma , after every key,value pair including the last pair. The rest is self explanatory as you can iterate through the maps as you would do with slices. To add a key, value, you declare credentials[url] = "https://facebook.com" which will add new pair to the map. To delete, you simply call delete(credentials, "url"). You would enter the map variable and the key you would want to delete.

Functions

Defining functions

package main

import "fmt"

func add(a int, b int) int {
    return a + b
}

func divmod(a int, b int) (int, int) {
    return a / b, a % b
}

func main() {
    val := add(1, 3)
    fmt.Println(val)

    div, mod := divmod(3, 2)
    fmt.Printf("div=%d, mod=%d\n", div, mod)
}

Above code shows you how to declare a function. When you declare a function, you must specify the variable type and it’s return type, as shown below:

func add(a int, b int) int

golang can also return more than one as shown above. You need to use parenthesis when specifying multiple return types.

Parameter passing

package main

import "fmt"

func doubleAt(values []int, i int) {
    values[i] *= 2
}

func double(x int) {
    x *= 2
}

func doublePtr(x *int) {
    *x *= 2
}

func main() {
    values := []int{1, 2, 3, 4}
    doubleAt(values, 2)
    fmt.Println(values)

    val := 10
    double(val)
    fmt.Println(val)
    doublePtr(&val)
    fmt.Println(val)
}

In above code, you pass parameters to the function to process the parameter. Take note that golang passes by value, that whatever you pass into a function, will be applicable within the function body. To change the value, you can use go pointers. You can declare a function like below, with pointer:

func doublePtr(x *int)

To pass the value of your variable into the function, you need to use & like below:

doublePtr(&val)

For slices however, the value is changed even if the slice is outside of the function.

Errors

package main

import (
    "fmt"
    "math"
)

func sqrt(x float64) (float64, error) {
    if x < 0 {
        return 0.0, fmt.Errorf("sqrt of negative value (%f)", x)
    }
    return math.Sqrt(x), nil
}

func main() {
    s1, err := sqrt(2.0)
    if err != nil {
        fmt.Printf("Error: %s", err)
    } else {
        fmt.Println(s1)
    }

    s2, err := sqrt(-2.0)
    if err != nil {
        fmt.Printf("Error: %s", err)
    } else {
        fmt.Println(s2)
    }
}

In above example, we declare a function to calculate the positive square root value. In the function header, we specify error return type to catch any error. In this case, if a number is negative, then the error part of the return will be provided. In main function, we declare a simple if..else statement to check if the err variable is empty. In second attempt s2, an error will be generated since it is a negative number.

Defer

package main

import (
    "fmt"
)

func cleanup(name string) {
    fmt.Printf("Cleaning up %s\n", name)
}

func worker() {
    defer cleanup("A")
    defer cleanup("B")
    fmt.Println("worker")
}

func main() {
    worker()
}

Golang has a garbage collector. Which means you don’t have to deal with memory management. When you allocate an object and then stop using it, Golang's garbage collector will clear it up. However, memory is just one kind of resource. And you may have other resources you use in your program. For example, files, sockets, virtual machines and others. You’d like to make sure that these resources are closed when you’re done with them as well. To make sure a resource is closed, use defer.

To think of defer, think of a process you would like to do after main process, say you want to authenticate a user then redirect the user to his/her homepage. You can you defer in this case, where the main function of authenticating of users will run first before running deferred function of show his/her homepage.

Above example shows a simple usage of defer. When you execute the code, the interpreter will print the worker print statement before calling cleanup("B") and then cleanup("A").

Structs

package main

import "fmt"

type credentials struct {
    Username   string
    Password   string
    isLoggedIn bool
}

func main() {
    credentailOne := credentials{
        "vgnshlvnz",
        "!@m@h@ck3r",
        false,
    }

    fmt.Printf("%+v\n", credentailOne)
    fmt.Println(credentailOne.Username)
    credentailOne.isLoggedIn = true
    fmt.Println(credentailOne.isLoggedIn)
}

There are times you’d like to define your own data structures. In golang you’ll use Struct to combine several fields into a datatype.

In example above, we defined a Struct called credentials and set some variable and it’s datatype. Then in function main, we created an instance of the struct with our values. We printed the values of the struct, printed individual variable from the struct and then changed the value of one of the variable in the struct.

If you’re coming from languages such as Java or C++, you might wonder if you can have private or protective fields which are not exposed outside of your package. In Go, this is very simple. Everything that starts with an uppercase letter, is accessible from other packages. Otherwise, it’s accessible only from within the current package.

Methods

package main

import "fmt"

type Trade struct {
    Symbol string
    Volume int
    Price  float64
    Buy    bool
}

func (t *Trade) Value() float64 {
    value := float64(t.Volume) * t.Price
    if t.Buy {
        value = -value
    }
    return value
}

func main() {
    t := Trade{
        Symbol: "MSFT",
        Volume: 10,
        Price:  99.98,
        Buy:    true,
    }
    fmt.Println(t.Value())
}

In above example, we have created a function called Value which accepts pointer struct Trade as t with a floating point return. A calculation is performed then if t.Buy value is true then the value is changed to negative.

# How To Install Go on Debian 10)
# Getting Started)

Written with StackEdit.

social