golang

Those are my personal notes on my journey to learn go and is not a full tutorial, i assume that you have already installed go on your system.

The simplest go program valid for compilation is this, save it as main.go

package mainfunc main() {
}

go give us two options, one is to compile the program into an executable and the second one is to run it without compilation (to be honest i believe that still compilation occurs somehow in the background)

In the following example we can see that compiling main.go file produces a binary file named main

kpatronas@narcissus:~/go/src/hello_world$ go build main.go 
kpatronas@narcissus:~/go/src/hello_world$ ls
main main.go
kpatronas@narcissus:~/go/src/hello_world$

If we run the executable will not produce anything on screen because our source file main.go had no any command that generate ouput on screen. lets try to print something, change the code to this

package mainimport "fmt"func main() {
fmt.Println("Hello world")
}

Now if we build and re-run the executable would produce “Hello world” on screen

kpatronas@narcissus:~/go/src/hello_world$ go build main.go 
kpatronas@narcissus:~/go/src/hello_world$ ./main
Hello world

We can run the program without compilation using the go run command

kpatronas@narcissus:~/go/src/hello_world$ go run main.go 
Hello world

go supports multi-line and single line comments

  • single line comments start with //
  • multi-line comments start with /* and end with */
package mainimport "fmt"/*
Multi
Line
Comment
*/
// Single line commandfunc main() {
fmt.Println("Hello world")
}

Simple strings in go are enclosed in double quotes

fmt.Println("Just a string")

Multi-line strings are enclosed over back-ticks and they can also contain “quotes”.

fmt.Println(`This
is
A
"multi-line"
string.`)

go supports unicode by design, this means that we can pass bytes that represent a unicode character and this character will printed on screen

fmt.Println("\u2272")

We can also print the hex number of a character using single quotes

fmt.Println('A')
65

When doing calculations with integers go will never return a float.

fmt.Println("Division:",20/3)
6

To return a float at least one of the numbers must be float

fmt.Println("Division:",20/3.0)
6.666666666666667

Boolean operators are operators that return trueor falseon execution, go supports the following operators

var1 == var2 // equal
var1 != var2 // not equal
var1 < var2 // less than
var1 > var2 // greater than
var1 >= var2 // greater or equal than
var1 <= var2 // less or equal than

Note that comparing an interger and a float will return results as expected.

There are two ways to declare variables the verbose way and the short way, the short way ommits the var keyword and the type of the variable

// The verbose way
var i int = 2
// The short way
j := 2

Using the verbose way we can declare numbers with an initial value of 0 and strings with zero length

var i int
var name string
fmt.Println("i",i)
fmt.Println("Name",name)

Executing this will output

i 0
Name

There is also the option to declare two or more variables in one line

var i,j,k int = 1,2,3
var l,m,n int
name, surname := "kostas","patronas"
_, whatever := "sdsds","just a string"

The “_” is called blank identifier and is used to inform go that intentionally we are not using this variable.

Arrays in go are sequences of elements of the same type, arrays always have specified length.

names := [3]string{"aaa","bbb","ccc"}

Values of array elements can be set after creation

var names [3]string
names[0] = "kostas"

Slices are variable length arrays and we can append elements dynamically

names := []string{}
names = append(names,"kostas")
names = append(names,"nick")
fmt.Println(names)
[kostas nick]

If we know the initial number of elements for a slice we can predefine them, this can be done with two ways, the first way is to use append with multiple arguments.

names := []string{}
names = append(names,"kostas","nikos")

The second way is to use the make function to preallocate the space

names := make([]string, 4)
names[0] = "kostas"
names[1] = "nikos"

To get the number of elements of a slice or an array we use the len function

fmt.Println(len(names))
2

Maps in go are what dictionaries are in Python, it is a non ordered sequence of elements that consist of a unique key that corresponds to a value

ages := map[string]int{
"kostas":39,
"nikos": 40,
}
fmt.Println(ages)
map[kostas:39 nikos:40]

Maps have variable length and we can add, read and remove items from a map dynamically.

To get the value of a key

fmt.Println(ages["nikos"])
40

To add an item

ages["pavlos"] = 25

To delete an itetm we will use the delete function, as arguments we need the name of the map and a key,

delete(ages, "kostas")

The if statement allows us to add conditional logic to our programs, the if statement evaluates given expressions to boolean values, if the value is true the code inside the if block will be executed, if its false that code will not run.

a := 1
if a == 1 {
fmt.Println("This will run because a is equal to one")
}

This code will print

This will run because a is equal to one

There is also the else statement that compliments if and instructs go what to do in case that the if statement evaluates to false

a := 2
if a == 1 {
fmt.Println("This will run because a is equal to one")
} else {
fmt.Println("This will run because a is not equal to one")
}

This code will print

This will run because a is not equal to one

Also there is the else if statement which allows us to do more complex conditional logic

a := 2
if a == 1 {
fmt.Println("This will run because a is equal to one")
} else if a == 2 {
fmt.Println("This will run because a is equal to two")
} else {
fmt.Println("This will run because a is not equal to one or two")
}

This code will print

This will run because a is equal to two

Having too many if statements can make our code very complex and hard to read, go provides the switch statement that can make our code easier to read and maintain. The above code can be writen with switch

a := 2
switch {
case a == 1:
fmt.Println("This will run because a is equal to one")
case a == 2:
fmt.Println("This will run because a is equal to two")
default:
fmt.Println("This will run because a is not equal to one or two")
}

This code will print

This will run because a is equal to two

The default statement will executed if no case statement evaluates to true, default can be ommited if there is no need to exist.

One thing you need to know about the switch behavior is that switch will not evaluate all cases, instead will stop evaluating as soon as a single case statement is true.

a := 10if a > 1 {
fmt.Println("if a is greater than 1")}
if a > 2 {
fmt.Println("if a is greater than 2")}
switch {
case a > 1:
fmt.Println("switch a is greater than 1")
case a > 2:
fmt.Println("switch a is greater than 2")}

As we can see both if statements executed because both evaluate to true, but using switch only the first case statement executed despite both cases can evaluate to true

if a is greater than 1
if a is greater than 2
switch a is greater than 1

Go supports only one loop type, the “for” statement, for syntax is like this

for var := init_statement; condition; post_statement

On a real world example this translates to something like this

for i:=1;i<=10;i++ {
//code
}
  • init_statement: runs once
  • condition: must be true to execute the code inside the for block
  • post_statement: runs after each iteration and usually is used to modify the variable created in init_statement and evaluated to the condition statement

Example: simple for loop

for i:=1;i<=3;i++ {
fmt.Println(i)
}

Output:

1
2
3

Example: looping an array

To loop an array or a map we need the range function

my_array := []int{1,2,3}for index,value := range my_array {
fmt.Printf("index: %d value: %d\n",index,value)}

Output:

index: 0 value: 1
index: 1 value: 2
index: 2 value: 3

Example: looping a map

my_map := map[string]int{}
my_map["kostas"] = 39
my_map["nikos"] = 40
for key,value := range my_map {
fmt.Printf("key: %s value: %d\n",key,value)}

Output:

key: kostas value: 39
key: nikos value: 40

Example: emulating the while statement

Not all arguments are required for the for statement, this allows us to be flexible and emulate the while functionality, in this example if a is equal to 5 then the for will exit the loop

a:=0
for a <= 5 {
fmt.Println(a)
a++
}

Functions are many commands grouped together, this allows us to re-use code without the need to re-write the code, functions might or might not get arguments and return results

Example: a function that greets someone (concat two strings)

Function hello_world gets a name which is a string, when is called it returns name concated with “Hello “

package main
import "fmt"
func main() {
h := hello_world("kostas")
fmt.Println(h)
}
func hello_world(name string) string {
return fmt.Sprintf("Hello %s",name)
}

Example: a function that does not accept or returns anything

package main
import "fmt"
func main() {
hello()
}
func hello() {
fmt.Printf("Hello!\n")
}

To read user input from the keyboard (stdin) we need to create a bufio.NewReader struct that will bind to os.Stdin

reader := bufio.NewReader(os.Stdin)

Then we will use the Reader.ReadString function to read from the struct

input_data, _ := reader.ReadString('\n')

And finally the strings.TrimSpace function will remove the trailing space from Stdin

input_data = strings.TrimSpace(input_data)

Example: read user input

package main
import "fmt"
import "bufio"
import "os"
import "strings"
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Input: ")
input_data, _ := reader.ReadString('\n')
input_data = strings.TrimSpace(input_data)
fmt.Println(input_data)
}

Running this will ask for “Input” and then will print input to the string

Input: hello
hello

Writing a file

The following example does the following

  1. Opens file data.txt using the following parameters
  • os.OpenFile tries to open file data.txt
  • os.O_CREATE instructs go to create the file if does not exist
  • os.O_WRONLY opens the file in write only mode
  • os.O_APPEND appends data to be written to the end of the file, if we ommit this all previous data from the file will be lost
  • 0644 are the permissions for the file

2. Then checks if there is an error while opening the file, if not execution continues, else the program exits

3. file.Write will write the data variable to the file as plain bytes

4. And finally we will check if write returned any errors

5. The defer statement is executed when the function (in this case main()) returns, in our case when main() function ends go will close file data.txt

import "fmt"
import "os"
.
.
.
data := "My message\n"
file, err := os.OpenFile("data.txt", os.O_APPEND | os.O_CREATE | os.O_WRONLY, 0664)if err != nil {
fmt.Println("Error: Unable to open data.txt")
os.Exit(1)}
defer file.Close()
_,err = file.Write([]byte(data))
if err != nil {
fmt.Println("Error: failed to write to data.txt")
os.Exit(1)}

Reading a file

The following example does the following

  1. Opens file data.txt using the following parameters
  • os.OpenFile tries to open file data.txt
  • os.O_RDONLY opens the file in read only mode
  • 0644 are the permissions for the file

2. Then checks if there is an error while opening the file, if not execution continues, else the program exits

3. bufio.NewScanner(file) creates a new scanner variable that will use to read the file line by line

4. for scanner.Scan() loop reads the file line by line as unicode and prints data to the screen

5. The defer statement is executed when the function (in this case main()) returns, in our case when main() function ends go will close file data.txt

import "fmt"
import "os"
import "bufio"
.
.
.
file, err := os.OpenFile("data.txt",os.O_RDONLY, 0664)
if err != nil {
fmt.Println("Error: Unable to open data.txt")
os.Exit(1)}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan(){
fmt.Println(scanner.Text())}

We will create a simple tool that accepts command line parameters to do some complex things, the tool can accept the following set of arguments

  1. filename,data: giving filename and data will write those data to the file
  2. filename: giving only filename will print the file contents to the screen

To parse the command line arguments we use the flag package which provides command line agruments parsing capabilities

First we need to declare the variables that will be used to store the arguments, then we use the flag.StringVar which defines a string argument

its syntax is

func StringVar(variable, "argument name", "default value", "usage")

Then we parse the arguments using the flag.Parse() function

var filename string
var data string
flag.StringVar(&filename, "filename", "", "File To Read or Write")
flag.StringVar(&data, "data", "", "Data To Be Written")
flag.Parse()

Here you can find the full example of the tool, i will not explain the code since is the combination of the previous examples to a single program.

package mainimport "fmt"
import "os"
import "bufio"
import "flag"
func main() {var filename string
var data string
flag.StringVar(&filename, "filename", "", "File To Read or Write")
flag.StringVar(&data, "data", "", "Data To Be Written")
flag.Parse()
if filename == "" {
flag.Usage()
os.Exit(1)}
if data == "" {
file, err := os.OpenFile(filename, os.O_RDONLY, 0664)
if err != nil {
fmt.Println("Error: Unable To Open ", filename)
os.Exit(1)}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())}
} else {
file, err := os.OpenFile(filename,os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664)
if err != nil {
fmt.Println("Error: Unable To Open ", filename)
os.Exit(1)}
defer file.Close()
data = fmt.Sprintf("%s\n", data)
_, err = file.Write([]byte(data))
if err != nil {
fmt.Println("Error: Failed To Write To ", filename)
os.Exit(1)}
}
}

Go allows us to compile the program for many platforms

Example: compile for Linux

GOOS=linux GOARCH=amd64 go build -o example -i example.go

Example: compile for Windows

GOOS=windows GOARCH=amd64 go build -o example.exe -i example.go

Example: compile for Mac

GOOS=darwin GOARCH=amd64 go build -o example -i example.go

I hope this small tutorial gave you an easy start to your journey on learning Go :)

DevOps engineer, loves Linux, Python, cats and Amiga computers

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store