Go Lang: Practical Reference

A quick reference to Go Programming Language

Tiago Albuquerque
5 min readOct 23, 2019

Introduction

This post is a personal reference to Go programming language to be used as a quick reminder of its keys topics. Hope it helps others developers with same issues. Enjoy!

This post assumes previous knowledge of programming logic and Go installation and development setup, as described here.

As its official description here:

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Go say “Hello World”

The base structure of a Go program is like this:

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

Variables

Go is a strong typed language. The basic types in Go lang are:

bool, string, 
int, int8, int16, int32, int64,
uint, uint8, uint16, uint32, uint64, uintptr,
byte, // alias for uint8
rune, // alias for int32
float32, float64,
complex64, complex128

To declare and assign a value to a variable can be done explicit or in the inferred way. For constants, it is used the word “const”:

// Syntax:
var
<var_name> <var_type> = <value> // explicit type
<var_name> := <value> // inferred type
// Examples:
var movieCounter int = 10
score := 4 // int
rate := 4.5 // float
const answerToLifeTheUniverseAndEverything = 42
// To show the value and variable type:
fmt.Printf("%v,%T",var,var)

All variables must be used, otherwise the program does not compile.

Arrays and Slices

Arrays and Slices are collection structures in Go. Both can access the elements by its index, and the elements must be from the same type. While arrays have fixed size, slices can have its capacity increased.

// Syntax:
<arr_name> := [<size>]<var_type>{<initializers>}
<arr_name> := make([]<var_type>,<length>)
<slice_name> := make([]<var_type>,<length>,<capacity>)
// Examples: // Arrays:
scores := [4]int{100,95,90,100} // initialization part is optional
scores := [...]int{100,95,90,100} // inferred array size
// Slices:
scores := []int{100,95,90,100} // slice has no fixed size
// Operations
var names [3]string
names[0] = "Tiago" // assign value to an array index
names[1] = "Obiwan"
names[2] = "Anakin"
names[3] = "Ahsoka"
s := make([]int, 4, 100)others := names[1:3] // slice the 2nd to 3rd elementlen(names) // gives the length of the array or slice
cap(names) // gives the capacity of the slice
append(array, value1, value2, valuen...) // append values to array

Maps

Maps stores information in key-value format:

// Syntax: 
<map_name> := map[<key_type>]<value_type> { <key-value entries> }
<map_name> := make(map[<key_type>]<value_type>)
// Examples:
bandFormedYear := map[string]int {
"Sepultura" : 1984,
"Slayer" : 1981,
"Iron Maiden" : 1975,
}
grades := make(map[string]int)
// Operations:
bandFormedYear["Sepultura"] // retrieves 1984
bandFormedYear["Epica"] = 2002 // adds the key-value pair
delete(bandFormedYear, "Epica") // deletes entry
len(bandFormedYear) // returns the lengthval, ok := bandFormedYear["NonExistent"] // 'ok' (boolean) indicates if value exists or not, since non-existing values always return '0'

Structs

With struct we can describe any type of data that we want.

// Syntax
type <struct_name> struct {
<field_name> <field_type>
...
}
// Examples
type Warrior struct {
Name string
Strength int
}
// Operations:
aWarrior:= Warrior {
Name: 'Archer',
Strength: 80,
}
anotherWarrior := {
"Swordsman",
90,
}
aWarrior.name // retrieves the property of the struct

Conditional statements

If statement in Go:

if <condition> {
<operations_if_true>
} else if <condition> {
<operations_if_true>
} else {
<operations_if_true>
}

Switch statement syntax in Go is as shown below. There is no break command, since there is no fall through execution on the case conditions.

switch <value> {
case
<value1>:
<operations>
case <value2>:
<operations>
default:
<operations>
}

Looping statement

Go has only one looping construct, the for loop.

// Syntax:
for <initializer>; <condition>; <incrementer> {//optional statements
<operations>
}
for
key, val := range <var_name> { // loop through collection vars
<operations>
}
// Examples:
for i := 0; i < 5; i++ {
fmt.Println(i) // prints 0 1 2 3 4
}
for {
// infinite loop
<operations until break keyword, if exists>
}

Flow Control

To alter the normal flow of a function, we can use the keyword defer:

// Function below prints 1 3 2
func main() {
fmt.Println("1");
defer fmt.Println("2");
fmt.Println("3");
}

To handle exceptional behavior, we can use the keyword panic to abort the normal flow of the program:

// Function below prints "abort"
func main() {
fmt.Println("begin")
panic("abort")
fmt.Println("end")
}

Pointers

We can declare and assign variables values by copying values or referencing its memory address:

// Example:
func main() {
var a int = 16;
var b *int = &a // b is an int pointer, which points to a address
var c := a
fmt.Println(a, *b, c) // prints 16 16 16
a = 32
fmt.Println(a, *b, c) // prints 32 32 16
}

Functions

The entry point of a Go program is the main function. It's important to know that functions parameters are passed by value, unless we use the variable address (by reference).

Functions basics syntax are as described below:

// Syntax
func <func_name>(<func_parameters>) (<return_types>) {
<operations>
}
// Examples:
func printSomething(value string) { // no return value, str param
fmt.Println(value);
}
func sum(values ...int) int { // int return, int values params
result := 0
for _, v := range values {
result += v
}
fmt.Println("Sum:", result)
return result
}
func
divide(a, b float64) (float64, error) { // many return vals
if b == 0.0 {
return 0.0, fmt.Errorf("Division by zero")
}
return a / b, nil
}

Methods

Methods are just functions that is executed in a known context:

type greeter struct {
message string
name string
}
func (g greeter) greet() { // method
fmt.Println(g.message, g.name)
}
func main() {
g := greeter {
message: "Hi",
name: "Tiago",
}
g.greet() // prints "Hi Tiago"
}

Interfaces

Interface describes behavior:

// Syntax: 
type <interface_name> interface {
<functions>
}
// Example:
type Writer interface {
Write([]byte) (int,error) // behavior contract
}
type
ConsoleWriter struct { }
// implicit implements interface (by method impl.)
func (cw ConsoleWriter) Write(data []byte) (int, error) {
n, err := fmt.Println(string(data))
return n, err
}
// using the interface reference
func main() {
var w Writer = ConsoleWriter{} // polymorphism
w.Write([]byte("Hi!"))
}

Concurrency

Go implements concurrency with goroutines, just by using the go keyword. The WaitGroup and Mutex are used to synchronize routines:

// Syntax:
func main() {
go <function_name> // executes this function asynchronously
}
// Examples:
var wg = sync.WaitGroup{}
func main() {
var msg = "Hi"
wg.Add(1)
go say(msg)go enjoy(“programming”)go enjoy(“programming”)
wg.Wait()
}
func say(msg string) {
fmt.Println(msg)
wg.Done()
}

In the context of goroutines, the communication can be done by channels to transmit data across the routines:

// Syntax: 
<channel_name> := make(chan <channel_type>, <buffer>)
// Examples:
var wg = sync.WaitGoup{}
func main() {
ch := make(chan int, 20) // channel of int message values
wg.Add(2) // there will be 2 concurrent routines
go func(ch <-chan int) { // receiver
for i := range ch {
fmt.Println(i) // prints '16' and '32'
}
wg.Done()
}(ch) // immediate anonymous function call
go func(ch chan<- int) { // sender
ch <- 16 // put values through the channel
ch <- 32
close(ch) // indicates we are done sending values
wg.Done()
}(ch) // immediate anonymous function call
wg.Wait()
}

Concurrency in Go Lang is really a big deal, and there is a lot more to know about. I intent to study and describe it better in a separate post.

go enjoy(“programming”)

--

--