Go

General

Go is a statically typed, compiled programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson.Go is syntactically similar to C, but with memory safety, garbage collection, structural typing and CSP-style concurrency.The language is often referred to as Golang because of its domain name, golang.org, but the proper name is Go.

It is good for:

  • networking (http,udp,tcp)
  • concurrency/paralilism
  • systems
  • automation, cli tools
  • crypto
  • image processing

Resources

Here is the list of resources which can be used on daily basis.

  • The Go programiming language book (intermedite/advanced)
  • Bill Kenedy’s book - Go in Action
  • Caleb Doxsey’s book - Introducting to Go
  • Go by example
  • Language spec - official documentation
  • Efective go - official documentation

Installation and configuration

Installing is done via package manger or by getting source code and following instructions from it.

Configuration

Following environment variables must be after go installation.

  • GOPATH - go workspace,any name can be used
  • GOROOT - holds golang source code

Under GOPATH structure should look like:

▶ tree -L 1 $(printenv GOPATH)
/home/mjv/go
├── bin 
├── pkg
└── src

where folders are:

  • bin - holds compiled binaries
  • pkg - holds archived files which programs pulls in all other code from other packages during compiling
  • src - holds source code

Commands

  • go env - print go env
  • go version - print go version
  • go help [topic] - print help
  • go doc <cmd> - print doc for cmd
  • go get [-d] package - download and [install] package
  • go fmt - formating the code
  • go run filaname - running the *.go code, it needs a filename
  • go build - build executable file in current folder
  • go install - compile and install package and dependencies and place it inside GOPATH/bin

Hello world

// Every go file begins with a package name. The name of the package must be the same as the folder name except for package main. Package main is the entry point for your program.
package main

// import fmt package
import (
	"fmt"
)

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

Modules

hello.go

package go

func Hello() string {
	return "Hello, world."
}

hello_test.go

package hello

import "testing"

func TestHello(t *testing.T) {
	want := "Hello, world."
	if got := Hello(); got != want {
		t.Errorf("Hello(): %q, want %q", got ,want)
	}
}
  • go mod init REPO - initialize module in go
  • direct dependency - what you import in *.go
  • indirect dependency - what was downloaded based on dependencies from directly imported modules
  • go list -m [-versions] all - list all dependencies (with versions as well)
  • go.mod - contains golang version used for module,require statement for imported modules and include/exclude statement for including/excluding files from the current module
  • go.sum - contains expected cryptographic hashes of the content of specific module versions
  • go get rsc.io/[email protected] - get module rsc.io/sampler with version v.1.3.1

Packages

Every program must have package main and func main(){} which is also an entry point of the program.When program leaves func main(){} your program is done.

  • func test(a ...interface{} -> here ...interface{} can be thought as i allows for an unknown number of arguments and any type
  • interface{} - empty interface
  • _ - throw away returns,this char allows you to tell to the compiler that you are not going to use this value returned by a function
  • fmt.Println = package.Identifier

Package name must match folder name (except package main) and must have func main(){}.Statenment of func main(){} can have other functions.Here are some words from go spec which are used for talking about the language:

  • identifiers(variable name) - first char must be letter
  • keywords - reserverd words by go which cannot be used as identifiers
  • type - every variable needs to have a declared type so go language can process the code based on its value
  • := - short declaration operator, declare and assign,latter for reassinging use =
  • statement - represents a line in go which are usually made of expressions ( e.g. x := 5 is a statement)
  • expression - y := 24 + 5 here 24 + 5 is an expression, it consists of an operator (+) and operands (24,5)

fmt package

group1 - general printing to standard out

  • fmt.Print
  • fmt.Println
  • fmt.Printf

group2 - printing to a string which you can assignt to a variable

  • fmt.Sprint
  • fmt.Sprintln
  • fmt.Sprintf

group3 - printing to a file or a web server response

  • fmt.Fprint
  • fmt.Fprintln
  • fmt.Fprintf

group4 - scan for input and store it to variables

  • fmt.Scan
  • fmt.Scanln
  • fmt.Scanf

Types

Conversions

Conversions are expressions of the form T(x) when T is a TYPE and x is an expression that can be converted to TYPE T.

Variables

Variables are created with the same pattern as:

// KEYWORD IDENTIFIER TYPE
type person struct // user-defiend type
var x int // int variable
const y string // string constant 

var keyword = var x string - defining variables outside the function, use for package scope or zero value

// DECLARE there is a VARIABLE with IDENTIFIER "z"
// and that the VARIABLE with IDENTIFIER "z" is of TYPE int
// ASSIGNS the ZERO VALUE of TYPE int to "z"
var z int

// DECLARE a VARIABLE is of a certain TYPE it can only hold VALUES of that TYPE
var x string

ZERO VALUES

  • false for booleans
  • 0 for integers
  • 0.0 for floats
  • "" for strings
  • nil for pointers,functions,interfaces,slices,channels,maps

Composite literal

x := 5 - here := is called short declaration operator. it is used for defining variables inside functions, their scope is limited to the related function

  • x := "hello" - string
  • i := 10 - int
  • b := true - bool
  • a := [5]int{1,2,3,4,5} - array
  • s := []string{"mile","kitic"} - slice
  • m := map[string]string{ "name": "mile" } - map
  • f := func(){ fmt.Println("this is func") } - func
  • st := struct{ first string last: string }{ first:"Mile", last:"Kitic" } - struct

Strings

  • raw_literal - s := `hello` - can include space,\n and other special chars,everything expect back quote
  • string_literal - s := "hello" - any char can may appear except newline and unescaped double quote
  • run_literal - s := 'h' - one or more characters enclosed in single quotes, as in ‘x’ or ‘\n’
  • byte = unit8
  • rune = int32

String value is a sequnece of bytes -> string = []byte

Code point

  • Unicode - Every platonic letter in every alphabet is assigned a magic number by the Unicode consortion which is written like U+0639 and it is called code point.Code point in UTF8 is a character, it is called rune in GO.Code points are represented in hex(base 16).
  • UTF-8 - storing your string of Unicode code points,in memory using 8 bits.In UTF-8,every code point from 0-127 is stored in a single byte.Only code points >128 are stored using 2,3,…6 bytes.If you have a string,in memory,in a file, or in a email message,you have to know what encoding it is in or you cannot interpret it or display it to the users correctly.

When you stora a character value in a string,we store its byte-at-a-time representation.Source code in Go is defined to be UTF-8 text, no other representation is allowed.That implies that whem, in the source code, we write the text

the text editor used to create the program places the UTF-8 encoding of the symbol into the source text.When we print out the hex bytes,we’re just dumping the data the editor placed in the file.

In short,GO source code is UTF-8,so the source code for the string literal is UTF-8 text.If that string literal contains no escape sequences, which is raw string cannot, the constructed string will hold exactly the source text between the quotes.Thus by definition and by the construction the raw string will always contain a valid UTF-8 respresentation of its contents.Similarly, unless it contains UTF-8-breaking escapes like them from the previous section, a regular string literal will also alywas contain valid UTF-8. To summarize,strings can contain arbitrary bytes,but when constructed from string literals,those bytes are (almost always) UTF-8 sequences.Those sequences represent Unicode code points, called runes.

package main

import "fmt"

func main() {
	s := "hello"
	fmt.Printf("value:%v\ntype:%T\nhex:% x\nlength:%v\nquoted:%+q\n", s, s, s, len(s), s)
	bs := []byte(s)
	fmt.Printf("dec_value:%v\ntype:%T\nunicode:%#U\nquoted:%+q\n", bs, bs, bs, bs)
	fmt.Println()
	s1 := `⌘`
	fmt.Printf("value:%v\ntype:%T\nhex:% x\nlength:%v\nquoted:%+q\n", s1, s1, s1, len(s1), s1)
	bs1 := []byte(s1)
	fmt.Printf("dec_value:%v\ntype:%T\nunicode:%#U\nquoted:%+q\n", bs1, bs1, bs1, bs1)
}

will print

value:hello
type:string
hex:68 65 6c 6c 6f
length:5
quoted:"hello"
dec_value:[104 101 108 108 111]
type:[]uint8
unicode:[U+0068 'h' U+0065 'e' U+006C 'l' U+006C 'l' U+006F 'o']
quoted:"hello"

value:⌘
type:string
hex:e2 8c 98
length:3
quoted:"\u2318"
dec_value:[226 140 152]
type:[]uint8
unicode:[U+00E2 'â' U+008C U+0098]
quoted:"\u2318"

A for range loop on a string decodes one UTF-8 encoded rune on each iteration.Each time around the loop, the index of the loop is the starting position of the current rune,measured in bytes,and the code point is its value. String are build from bytes so indexing them yields bytes,not charactes.A strint might not even hold characters.

  const nihongo = "日本語"
    for index, runeValue := range nihongo {
        fmt.Printf("%#U starts at byte position %d\n", runeValue, index)
    }

will print

U+65E5 '日' starts at byte position 0
U+672C '本' starts at byte position 3
U+8A9E '語' starts at byte position 6

Formating

  • %#U - utf
  • %#b - binary
  • %#o - octal
  • %#d - decimal
  • %T - type
  • %#x - hex
  • % x - hex with space between chars
  • %q - will escape any non-printable byte sequence’s in a string

Coding scheme

  • ascii - 128chars (0-127) - print the table with man 7 ascii
  • utf-8 - useng 1-4 bytes for representing the chars,first 128chars are mapped 1to1 to ascii

Constants

  • untyped - The default type of an untyped constant is bool, rune, int, float64, complex128 or string respectively, depending on whether it is a boolean, rune, integer, floating-point, complex, or string constant.
const a = 42
  • typed - means that we specified TYPE of the value
const a int = 42

Iota

iota - predefined identifier representing the untyped int

const (
	a = iota // a = 0
	b	 // b = 1
	c = iota // c = 2
)

Bits shifting

func main() {
	x := 10
	fmt.Printf("x_value:%d\t\tx_binary:%b\n",x,x)
	y := x << 1
	fmt.Printf("y_value:%d\t\ty_binary:%b\n",y,y)
}

will output

x_value:10		x_binary:1010
y_value:20		y_binary:10100 // one 0 is added to the end of the x_binary to set y_binary

Numbers

var x := 5

Booleans

var test := true

Constants

Cannot be changed.

const x string = "Hello World"

Control structures

If

if-else.go

if i % 2 == 0 {
  // even
} else {
  // odd
}

if-else-if.go

if i % 2 == 0 {
 // action
} else if i % 3 == 0 {
 // action
}

if-condition.go

// if var declaration;condition
if x := 100;x == 1000 { do something }

Switch

switch.go

switch i {
case 0: 
  fmt.Println("Zero")
case 1: 
  fmt.Println("One")
case 2: 
  fmt.Println("Two")
case 3: 
  fmt.Println("Three")
case 4: 
  fmt.Println("Four")
case 5: 
  fmt.Println("Five")
default: 
  fmt.Println("Unknown Number")
}

For

Go has only for loop.While,do and until can be created via for loop.

for.go

// for init statement(optional);evaluation;post statement (optional)
for i := 1; i <= 10; i++ {
    fmt.Println(i)
  }

Composite data (made up of other type - built-in or user-defined types)

reflect.TypeOf(<some_type>).Kind() - can be used to print the underlaying type(usefull for slice,arrays,maps,struct)

Arrays

An array is a numbered sequence of elements of a single type with a fixed length.It is zero based index record.

declaring-array.go

// x array needs an initiliazation also (e.g. x[i] = some_int)
var x [5]int

// array format arrayName := [arrayLength]arrayType{arrayElements}
x := []int{} // -> []
x := []int{0} // -> [0]

y := [5]float64{ 98, 93, 77, 82, 83 } // -> [98,93,77,82,83]

// or 
y := [5]float64{
  98,
  93,
  77,
  82,
  83,
}

for-range.go

// x is array, i is the index of the array and v is the value on the related index in the array
for i, v := range x {
  // some action
}

WARNING: Go compiler doesn’t allow to declare a variable which wont’be used(i variable in above case).In that case one can use _ as special variable which tells compiler that we don’t need it.

Slice

A slice is a segment of array but it is not an array.Like arrays slices are indexible and have length.Unlike arrays this length is allowed to change.Slice is 3 word data type which consists of:

  • pointer to underlaying array
  • length - length of the slice
  • capacity - allocated space for slice data

To create a slice one has several options:

  • var x []int - with var
  • x := make([]int,5) - with make builtin function where one should specify length and optionaly capacity.It will allocate and initialize the memory for the slice with zeroed values of related TYPE
  • xptr = new(int) - with new builtin function which will allocate memory and return point

slice.go

// one option for creating slice
var x []float64 // -> []

// preferred way for creating slice
x := make([]float64,5)

// composite literal,creates new instance each time it is evaluated 
// x := type{value}
x := []int{1,2,3,4} // -> [1 2 3 4]

// slicing - slice can be created with operator [low:high] as well. low is starting position,high is until position
x := []int{0,0,0,0,0}
y := x[0:3] // -> [0 0 0 0]

// make can use 3rd param for capacity which represents the capacityof underlying array.
// in case slice is extening its capacity Go will double capacity on the fly
x := make([]float64, 5, 10)

WARNING: slice is always associated with some array,and although they can never be longer than array, they can be smaller.

multidimensionalslice.go or slice of slices

x := []int{1,2,3} // -> [1 2 3]
y := []int{4,5,6} // -> [4 5 6]
xy := [][]int{x,y} // -> [ [1 2 3] [4 5 6]]

Functions on slices

append.go

// append will add a new item to the end of the slice.
slice1 := []int{1,2,3} // -> [1,2,3]
slice2 := append(slice1, 4, 5) // -> [1,2,3,4,5] 
slice1 = append(slice1, 4) // -> [1,2,3,4]
// slice2... is needed as append is expecting variadic number of elements and not a slice
slice1 = append(slice1, slice2...) // -> [1 2 3 1 2 3 4 5]

copy.go

slice1 := []int{1,2,3} // -> [1,2,3]
slice2 := make([]int, 2) // -> [0,0]
// content of slice1 will be copied to slice2 based on length of slice2
copy(slice2, slice1) // -> slice2 -> [1,2]

delete.go - with slicing and append

slice := []int{1,2,3,4,5,6} // -> [1 2 3 4 5 6]
slice := append(slice[:2], slice[4:]...) // -> [1 2 5 6]

Maps

A map is an unordered collection of key-value pairs.

Functions on maps

map.go

// creating a map with composite literal style - map[KEY_TYPE]VALUE_TYPE
m := map[string]int{
  "Mile": 10,
  "Kitic": 21,
}
fmt.Println(m) // -> m = {"Mile":10,"Kitic":21}

// creating a map with make(map(KEY_TYPE)VALUE_TYPE)
m := make(map[string]int)
m["Mile"] = 10
m["Kitic"] = 21
fmt.Println(m["Mile"]) // -> 10

// accessing with m[KEY]
fmt.Println(m["Mile") // -> 10

// checking if key exists in the map via comma ok method, `ok` value is boolean type which will be evaluated as true or false depending on existance of the related key in the map 
if v, ok := map["Mile"]; ok {
  fmt.Println(v)
}

// adding a new item to the map
m["Mira"] = 20
fmt.Println(m) // -> m = {"Mile":10,"Kitic":21,"Mira":20}

// deleting an item from the map,it is recommended to check if key exists
if v,ok := m["Mira"]; ok { // if key "Mira" exists ok will be evaluated as true
  fmt.Println(v)
  delete(m, "Mira") // delete the key "Mira"
}

Range

Range iterates over elements in a variety of data structures

range.go

for i,v := range string // iterating over strings,i is starting position of the rune and v is its code point
for i, num := range array // iterating over array,i is index and num is value
for _,num := range slice/array // iterating over slice/array
for k,v := range map // iterating over map, k is key, v is value.If any of them is not needed it can be replaced with `_`.

Structs

A struct is a composite data type which contains named fields, each of which has a name and a type.It allows us to compose together values of different types.A field declared with a type but no explicit filed name is called an embedded field or anonynmous field.An embedded field must be specified as a type name T or as a pointer to a non-interface type name *T.

  • named struct - define a named struct type
// creating a new struct type
type person struct{
  first string // name and the type
  last string
  age int
  gender string
}
func main(){
  // use composite literal person{}
  p1 := person{ first:"James",last:"Bond",age:42,gender:"male",}
  // do something...  
}

Go Playground struct

  • embedded struct - creating a type struct which defined type is using another user-defined struct
// user defined struct 
type person struct {
	first  string // name and the type
	last   string
	age    int
	gender string
}

// another user defined struct which is using above struct person
type secretAgent struct {
	person // embedded or annonymous type,specifying only the type not and not the name from another struct
	// license to kill
	ltk bool
}

func main() {
	sa1 := secretAgent{
		person: person{
			first:  "James",
			last:   "Bond",
			age:    42,
			gender: "male",
		},
		ltk: true,
	}
	// do someting
}

Go Playground embedded struct

  • anonymous struct - struct which doeasn’t have a name and it is not defined with type NAME struct {}
func main() {
	// defining a struct during initialization instead of `type NAME struct {}`
	p1 := struct {
		first  string
		last   string
		age    int
		gender string
	}{
		first:  "James",
		age:    10,
		gender: "male",
		last:   "Bond",
	}
	fmt.Println(p1)
}

Go Playground anonymous struct

Go OOP clarification

Is go an object oriented languate.Yes and no.Go has OOP aspects.But it’s all about TYPE.We create TYPES in Go; user-defined TYPES.We can then have VALUES of that type.We can assign VALUES of a user-defined TYPE to VARIABLES.

Functions

Functions are all about modules or small codes.When we define functions we use parameters but when we call functions we are using arguments.Functions may have return statement which is terminating the execution of current function,and optionally provides one or more result values.

func (

We can use function in package scope as well like

package main

import "fmt"

var f func() = func(){
	fmt.Println("aaa")
}

func main(){
	f() // -> "aaa"
	fmt.Printf("type:%T\n",f) // -> type: func()
}

Go Playground func

func main() {
	// basic func
	foo()

	// func with arguments
	bar("Mile")

	// func with return
	s1 := woo("Kitic")
	fmt.Println(s1)

	// func with multiple returns
	x, y := foobar("Mira", "Skoric")
	fmt.Println(x)
	fmt.Println(y)
}

// func format
// func (r receiver) identifier(parameters(s)) (return(s)) { ... }
// foo is identifier,there are no parameters and no returns
func foo() {
	fmt.Println("Hello from foo")
}

// scope of s string is limiter to the function bar
// bar is identifier,(s string) is parameter,there are no returns
func bar(s string) {
	fmt.Println("Hello from bar,", s)
}

// scope of s string is limited to function woo
// woo is identifier, (s string) is parameter,string is return
func woo(s string) string {
	return fmt.Sprint("Hello from woo, ", s)
}

// scope of the fn,ln,a and b variables are limited to foobar func
// in case we return multiple values we have to put them between `()`
// foobar is identifier,fn and ln are paramters, string and bool are return values
func foobar(fn string, ln string) (string, bool) {
	a := fmt.Sprint(fn, " ", ln, `, says "Hello"`)
	b := false
	return a, b
}

Go Playground functions

variadic.go

func main(){
  foo(1,2,3,4,5)
}
// ...int means it can receive multiple int parameters
func foo(x ...int) {
  fmt.Println(x)
  fmt.Printf("type:%T\n",x)
}

Go Playground variadic

unfurling.go

func main() {
	xi := []int{1, 2, 3, 4, 5}
	// passing elemnts of xi via xi...
	x := sum(xi...)
	fmt.Println(x)

}
// func sum is expecting 0 or more number of int as parameters
func sum(x ...int) int {
	fmt.Println(x)
	fmt.Printf("type:%T\n", x)
	sum := 0
	for _, v := range x {
		sum += v
	}
	return sum
}

Go Playground unfurling

Anonymous

Anonymous functions can accept parameters,return data, and do pretty mych anything else a normal function can do.You can even assign a regular function to a variable just like you do with an anonymous function.

anonymous.go

func main() {
	// anonymoyus func without arguments
	func() {
		fmt.Println("anonymous func")
	}()

	// anonymous func with argument
	func(x int) {
		fmt.Println("the meaning of life:", x)
	}(32) // we need to pass an argument to the anonymous function

	// inititalizaion and anonymous func with arguments - func expression - or assigning a function to a variable
	add := func(x, y int) int { // type of add is func
		return x + y
	}
	fmt.Println(add(3, 5))
}

Go Playground anonymous

Returning a values from a func

returning_string.go

func main(){
  s1 := foo()
  fmt.Println(s1)
}

func foo() string {
  s := "Hello world"
  return s
}

Go Playground returing a string

returning_func.go

func main() {
	s1 := foo()
	fmt.Println(s1)

	x := bar()                          
	fmt.Printf("type of x:%T\n", x)     // -> func() int
	fmt.Printf("type of x():%T\n", x()) // -> int
}

func foo() string {
	s := "Hello world"
	return s
}

// returning a type "func() int" as value
func bar() func() int {
	return func() int { // retrurning "func() int" to bar() and expecting int as returns
		return 42 // returning int to above anonymous func
	}
}

Go Playground returning a func

Callback

Passing a function as an argument to another function is callback.

callback.go

func main() {

	ii := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
	s := sum(ii...) // providing []int as argument
	fmt.Println(s)
	s1 := even(sum, ii...) // providing func sum as an argument
	fmt.Println(s1)
	s2 := odd(sum, ii...) // providing func sum as an argument
	fmt.Println(s2)

}

// func sum is expecting []int as paramter
func sum(xi ...int) int {
	total := 0
	for _, v := range xi {
		total += v
	}
	return total
}

// func even is expecting func and []int as parameters
func even(f func(xi ...int) int, vi ...int) int {
	var yi []int
	for _, v := range vi {
		if v%2 == 0 {
			yi = append(yi, v)
		}
	}
	return f(yi...)
}

// func odd is expecting func and []int as parameters
func odd(f func(xi ...int) int, vi ...int) int {
	var yi []int
	for _, v := range vi {
		if v%2 == 1 {
			yi = append(yi, v)
		}
	}
	return f(yi...)
}

Go Playground callback

callback2.go

func main() {

	g := func(xi []int) int {
		if len(xi) == 0 {
			return 0
		}
		if len(xi) == 1 {
			return 1
		}
		return xi[0] + xi[len(xi)-1]
	}

	// passing a g and slice of int ([]int) as argumetns to func foo() 
	f := foo(g, []int{1, 2, 3, 4, 5, 6, 7})
	fmt.Println("f:",f)

}

// func foo() accepts parameters as `func(xf []int) int` which match definition of g (`func(xi []int) int`) and slice of int ([]int)
func foo(f func(xf []int) int, ii []int) int {
	n := f(ii)
	n++
	return n
}

[Go Playground callback2

Closure

A clousure is a special type of anonymous function that references variables declared outside of the function itself.That means that we will be creating a function dynamically, but in this case we will be using variables that weren’t passed into the function as a parameter, but instead were available when the function was declared.

closure.go

func main() {
  n := 0
  counter := func() int {
    n += 1
    return n
  }
  fmt.Println(counter())
  fmt.Println(counter())
}

Notice how our anonymous function has access to the n variable, but this was never passed in as a parameter when counter() was called. This is what makes it a closure! Another interesting fact about the clousure functions is that they can still reference variables that they had access to during creation even if those variables are no longer referenced elsewhere.

closure-2.go

func main() {
  counter := newCounter()
  fmt.Println(counter()) // -> 1
  fmt.Println(counter()) // -> 2
  fmt.Println(counter()) // -> 3
  counter2 := newCounter()
  fmt.Printf("%T\n",counter2) // -> func() int
  fmt.Printf("%T\n",counter2()) // -> int
  fmt.Println(counter2()) // -> 1
  fmt.Println(counter2()) // -> 2
  fmt.Println(counter2()) // -> 3
}

func newCounter() func() int {
  n := 0
  return func() int {
    n += 1
    return n
  }
}

Go Playground clousure-2

In this example our closure references the variable n even after the newCounter() function has finished running. This means that our closure has access to a variable that keeps track of how many times it has been called, but no other code outside of the newCounter() function has access to this variable. This is one of the many benefits of a closure - we can persist data between function calls while also isolating the data from other code.

Recursion - function which is calling itself

recursion.go

func main(){
  f := factorial(4)
  fmt.Println(f) // -> 24 (4*3*2*1)
}

func factorial(x uint) uint {
  if x == 0 {
    return 1
  }
  return x * factorial(x-1)
}

Go Playground recursion

Defer,Panic and Recover

Go has a special statement called defer which schedules a function call to be run after the function completes.

defer.go

func first() {
  fmt.Println("1st")
}
func second() {
  fmt.Println("2nd")
}
func main() {
  defer second() // defer moves function second() to the end of the `main` function
  first()
}

Defer is ofter used when resources need to be freed in some way ( e.g. closing an opened file).Also, defer functions are run even if a run-time panic occurs.

panic_and_recover.go

func main() {
  defer func() {
    str := recover() // handling a run-time error
    fmt.Println(str)
  }()
  panic("PANIC") // causing run-time error
}

Pointers

Shortly, the pointer is a variable which stores an address of another variable, where some data is stored.

// set value for i to 10
// set memory location where value for variable i is stored to addri (&i) 
// *&i - give me the value which is stored on memory location of &i
i := 10
x := new(int)
x = &i 
// value of x is set to to addri (&i)
// &x - `&` is address operator and it is referencing to a memory address where x value is stored
// *x - `*` is dereferencing operator and it is used to access the value stored in x. as value stored in x is set to addri,*x is using to access to the value stored on memory location addri (or value of i)
// x has a TYPE of *int which is saying that this is a pointer to address where int value is stored

pointer.go

func main() {
  x := 100

  fmt.Printf("xtype: %T  xvalue: %v  xaddress: %p\n", x, x, &x) // -> xtype: int  xvalue: 100  xaddress: 0xc00002c008
  fmt.Println()

  y := new(int)
  fmt.Printf("ytype: %T  yvalue: %v  yaddress: %p  %v\n", y, y, &y, *y) // -> ytype: *int  yvalue: 0xc00002c048	yaddress: 0xc00000e030 0
  fmt.Println()
	
  y = &x
  fmt.Printf("ytype: %T  yvalue: %v  yaddress: %p  %v\n", y, y, &y, *y) // -> ytype: *int  yvalue: 0xc00002c008	yaddress: 0xc00000e030 100
}

Variable x of TYPE int has VALUE of 100 of the same type.Its value ADDRESS in memory is stored at 0x0201. After initialization and assignment via statement

var y *int = &x

variable y is getting a TYPE *int and its value ADDRESS in memory is stored ad 0x0206.Also, y variable value (*y) has VALUE equal to memory location of ADDRX.Thus, with *y we can access to the value stored at memory location ADDRX, which is now 100. The * operator is a dereference operator. The dereference here means that we are getting a value not of a pointer (which stores an address) but from the memory address where this pointer is… Well — pointed to :-).In previous example *y is getting a value from the memory address of variable y which was set to ADDRX. The & is an address operator which returns a variable’s memory address. Pointers are rarely used with Go’s built-in types, but they are extremely useful when paired with structs.

When to use pointers:

  • when you have large chunk of data and you don’t want to pass that big chunk of data around through your program, you just pass that address where that data is stored
  • you need to change something that’s at certain location

Methods

Methods provide a way to add behaviour to user-defined types(i.e struct).Methods are really functions that contain an extra parameter that’s declared between the keyword func and the function name.That extra parameter is called receiver.Receiver can be provided as value or pointer variable.Since methods often need to modify their receiver, pointer receivers are more common than value receivers.

Format of method functions is

//func (r receivers) identifier(parameters) (return(s)) { ... }

method.go

type person struct {
	first    string
	last     string
	age      int
	children []string
}

type secretAgent struct {
	person
	ltk bool
}

// func (r receiver) identifiers(parametes) (return(s)) { code }
// receiver will attach Speak() to the struct secretAgent
// any values of that TYPE(secretAgent) has access in this func
func (s secretAgent) Speak() {
	fmt.Println(s.first, s.last, s.age)
	for i, v := range s.children {
		fmt.Println(i, v)
	}
}

func main() {
	sa1 := secretAgent{
		person: person{
			first:    "Mile",
			last:     "Kitic",
			age:      42,
			children: []string{"lea", "mia"},
		},
		ltk: true,
	}

	fmt.Println(sa1)
	// any value of sa1 has access to the function speak()/method
	sa1.Speak()
}

Go Playground methods

value_and_pointer_methdos.go


// defining a struct object
type Vertex struct {
	X, Y float64
}

// defining a method function with value receiver.Thus, it will NOT change the value of the receiver's values
// v Vertex - receivers,Abs() is identifier without parameters, float64 is return
func (v Vertex) Abs() float64 {
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

// defining method function with `pointer` receiver.Thus, it will change the value of the receiver's values
// v *Vertes - receivers,Scale is identifier with float64 parameter
func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}

	// v type is main.Vertex
	// method Scale is expecting pointer receiver (*main.Vertex)
	// as v variable is addressable (has &v) Go lang is converting v.Scale(10) automaticaly to (&v).Scale(10) as Scale method is expecting pointer receiver
	// regardless of v type (value or pointer) on calling method Scale with value or pointer receiver,Golang will use pointer receiver as Scale method only support pointer receiver
	v.Scale(10) // methods with pointer receivers take either a value or a pointer as the receiver

	// method Abs() is expecting value receiver (main.Vertex)
	// as v is value receiver Go can 
	fmt.Println(v.Abs())
        p := &v
        p.Scale(10)

	// p type is *main.Vertex
	// as p can dereference value stored on v via *p Go is automatically converting p.Abs() to (*p).Abs() as method Abs() is only working with value receivers
	fmt.Println(p.Abs()) // methods with value receivers take either a value or a pointer as the receiver
}

Go Playground value and pointer receivers

Method sets

  • method set of an interface type is its interface
  • method set of any other type T consist of all methods declered with receiver type T
  • method set of the corresponding point type *T is a set of all methods declared with receivers *T and T
  • method set of a type determines the interface that the type implements and the methods that can be called using a receiver of that type

Methods may be declaerd on any named type defined in the same package, so long as its underlaying type is neither a pointer nor an interface.

In summary, in every valid method call expression,exactly one of these three statements is true:

  • receiver argument has the same type as the receiver parameter,for example both have type T or both have type *T
  • receiver argument is a variable of type T and the receiver parameter has type *T -> implicit (&variable)
  • receiver argument has type *T and the receiver paramter has type T.The compiler implicitly derefernces the receiver,or loads the value -> implicit (*variable)

Pointer vs Values receivers

The rule about pointer vs values for receivers is that value methods can be invoked on pointers and values, but pointers methods can only be invoked on pointers.This rule arises because pointer methods can modify the receiver;invoking them on a value would cause the method to receive a copy of the value, so any modifications would be discarded.There is a handy exception.When the value is addressable (has (&var) the language takes care of the common case of invoking a POINTER METHOD on a value by inserting the address operator automatically. When you declare a method using a value receiver, the method will always be operating against a copy of the value used to make the method call.When you call a method declared with a pointer receiver the value used to make the call is shared with the method.

Addressable values:

  • variable
  • *ptr
  • slice indexing
  • field selector (of struct)
  • array indexing

Non-addressable values:

  • &map["key"] - values in map
  • &afunc() - return values from func
  • &t.method() - method calls of var t

A value is addressable if it is an element of a slice,an element of an addresable array,a field of an addressable struct, or the result of dereferencing a pointer.

Interfaces

Interfaces are named collections of method signatures.It allow us to define behaviour.Interface allow a VALUE to be of more than one TYPE interface joke - hey baby,if you get this METHOD,you are my TYPE

interfaces_explained.go

// creating person struct with related fields
type person struct {
	first    string
	last     string
	age      int
	children []string
}

// creating secretAgent struct with embedded person struct and related fields
type secretAgent struct {
	person
	ltk bool
}

// format for method fund - func (r receiver) identifiers(parametes) (return(s)) { code }
// receiver will attach Speak() to the struct secretAgent
// any values of that TYPE(secretAgent) has access in this func
// this func has no return values
func (s secretAgent) Speak() {
	fmt.Println("I'am", s.first, s.last, " - the secretAgent Speak")
}

// this func has no return values
// receiver will attach Speak() to the struct person
// any values of that TYPE(person) has access in this func
func (p person) Speak() {
	fmt.Println("I'am", p.first, p.last, " - the person Speak")
}

// anyone who has this TYPE(interface) has this method(Speak()) is also of TYPE human
type human interface {
	// attach this method to the human interface
	// if Speak() has return values we would specify them here
	Speak()
}

// this function can take many different types, it depends on the methods interfaces and methods which are defined
func bar(h human) {
	switch h.(type) {
	case person:
		// assertion (h.(person).first) is you are asserting something of a certain type
		fmt.Println("I was passed into bar as person:", h.(person).first)
	case secretAgent:
		fmt.Println("I was passed into bar secretAgent with ltk:", h.(secretAgent).ltk)
	}
	fmt.Println("I was passed into func bar", h)
}

func main() {
	sa1 := secretAgent{
		person: person{
			first:    "Mile",
			last:     "Kitic",
			age:      42,
			children: []string{"lea", "mia"},
		},
		ltk: true,
	}
	// p1 is getting a value of type person
	p1 := person{
		first: "Mitar",
		last:  "Miric",
	}

	fmt.Println("sa1:", sa1)

	// any value of sa1 has access to the function Speak()/method
	sa1.Speak()

	fmt.Println("p1:", p1)

	// sa1 is getting a value of type human
	// sa1 is getting a vlues from both types, human and secretAgent
	bar(sa1)
	// p1 is getting a value of type human
	// p1 is getting a values from both types, human and person
	bar(p1)
}

Go Playground interfaces

interfaces.go

// defining interface 
type geometry interface {
  // method set - list of methods(functions) that a type must have in order to "implement" the interface 
  area() float64 // defining method, as area() has return value we have to specify float64 as well 
  perim() float64 // defining method, as perim() has return value we have to specify float64 as well
}

type rect struct { 
  width, height float64 
}
type circle struct { 
  radius float64 
}

// defining area() method for rect struct
func (r rect) area() float64 {
  return r.width * r.height
}
// defining perim() method for rect struct
func (r rect) perim() float64 {
  return 2*r.width + 2*r.height
}

// defining area() method for circle struct
func (c circle) area() float64 {
  return math.Pi * c.radius * c.radius
}

// defining perim() method for circle struct
func (c circle) perim() float64 {
  return 2 * math.Pi * c.radius
}

func measure(g geometry) {
  fmt.Println(g)
  fmt.Println(g.area())
  fmt.Println(g.perim())
}

func main() {
  r := rect{width: 3, height: 4}
  c := circle{radius: 5}

  // calling measure function with r as argument
  measure(r)
  // calling measure function with c as argument
  measure(c)
}

Empty interface

The interface type that specifies zero methods is known as the empty interface -> interface{}. An empty interface may hold values of any type.(Every type implements at least zero methods.)Empty interfaces are used by code that handles values of unknown type (e.g. fmt.Print takes any number of argumens of interface{}). Under the hood, interface values can be thought of as a tuple of a value and a concrete type -> (value, type)

empty_interface.go

func main() {
	var i interface{}
	describe(i) // -> (<nil>, <nil>)

	i = 42
	describe(i) // -> (42,int)

	i = "hello"
	describe(i) // -> (hello, string)
	
	i = [3]int{1,2,3}
	describe(i) // -> ([1 2 3], [3]int)
}

func describe(i interface{}) {
	fmt.Printf("(%v, %T)\n", i, i)
}

Type assertion

For an expression x of interface type and a type T, the primary expression:

x.(T)

asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion. More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T. If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T.

A type assertions provides access to an interface value’s underlying concrete value:

t := i.(T)

where the inteface value i holds the concrete type T and assigns the underlying T value to the variable t.To test whether an interface value holds a specific type following code is used:

t,ok := i.(T)

or type switch as

switch v := i.(type) {
case T:
    // here v has type T
case S:
    // here v has type S
default:
    // no match; here v has the same type as i
}

type-switch.go

func do(i interface{}) {
	switch v := i.(type) {
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main() {
	do(21)
	do("hello")
	do(true)
}

Applications

JSON

Marshaling - transformin the memory representation of an object to a data format suitable for storage or transmission.Marshaling in go is used to encode/convert to json.

func main() {
	type ColorGroup struct {
		ID     int		// field names must be uppercase if we want to export them from the package
		Name   string		// field names must be uppercase if we want to export them from the package
		Colors []string		// field names must be uppercase if we want to export them from the package
	}
	group := ColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
	}
	b, err := json.Marshal(group)
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b) // -> {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]}
}

Go Playground marshal

Unmarshaling is used to decode/convert an json object to struct.

func main() {
	var jsonBlob = []byte(`[
	{"Name": "Platypus", "Order": "Monotremata"},
	{"Name": "Quoll",    "Order": "Dasyuromorphia"}
]`)
	type Animal struct {
		Name  string		// field name must be uppercase if we want to export them from the package
		Order string		// field name must be uppercase if we want to export them from the package
	}
	var animals []Animal
	err := json.Unmarshal(jsonBlob, &animals)
	if err != nil {
		fmt.Println("error:", err)
	}
	fmt.Printf("%+v", animals)
}

Go Playground unmarshal

Encoder - an encoder writes JSON values to an output stream.

type user struct {
	Name string
	Age  int
}

func main() {
	p1 := user{
		Name: "Mile",
		Age:  10,
	}
	p2 := user{
		Name: "Mira",
		Age:  20,
	}

	users := []user{p1, p2}

	// create *json.Encoder using os.Stdout as io.Writer interface
	enc := json.NewEncoder(os.Stdout)
	// formating the output to have prettier output
	enc.SetIndent("","\t")
	// similar as json.Marshal, enc.Encode is used for encoding JSON to os.Stdout
	err := enc.Encode(users)
	if err != nil {
		fmt.Println(err)
	}
}

Go Playground Encoder

Decoder - reads and decodes JSON values from an input stream.

type Message struct {
	Name string
	Text string
}

func main() {
	var jsonStream = `
	{"Name": "Ed", "Text": "Knock knock."}
	{"Name": "Sam", "Text": "Who's there?"}
	{"Name": "Ed", "Text": "Go fmt."}
	{"Name": "Sam", "Text": "Go fmt who?"}
	{"Name": "Ed", "Text": "Go fmt yourself!"}`

	// *decoder needs io.Reader as input
	dec := json.NewDecoder(strings.NewReader(jsonStream))
	fmt.Printf("%T\n", dec)

	for {
		// reading line by line until EOF
		var m Message
		// similar as json.Unmarshal, dec.Decode is used for decoding JSON value and store it to variable m
		// io.EOF is used to read until EOF 
		if err := dec.Decode(&m); err == io.EOF {
			break
		} else if err != nil {
			fmt.Println(err)
		}
		fmt.Printf("%s: %s\n", m.Name, m.Text)
	}
}

Go Playground Decoder

Writer interface

A type satisfies an interface if it possesses all the methods the interface requires.*os.File has methods for Write,Close and Read

 go doc -short os.File.Write
func (f *File) Write(b []byte) (n int, err error)
    Write writes len(b) bytes to the File. It returns the number of bytes
    written and an error, if any. Write returns a non-nil error when n !=
    len(b).

 go doc -short os.File.Close
func (f *File) Close() error
    Close closes the File, rendering it unusable for I/O. On files that support
    SetDeadline, any pending I/O operations will be canceled and return
    immediately with an error. Close will return an error if it has already been
    called.

 go doc -short os.File.Read
func (f *File) Read(b []byte) (n int, err error)
    Read reads up to len(b) bytes from the File. It returns the number of bytes
    read and any error encountered. At end of file, Read returns 0, io.EOF.

We can check what methods are supported by type *os.File by triggering following code:

func main() {
	Print(os.Stdout)
}

func Print(x interface{}) {
	v := reflect.ValueOf(x)
	t := v.Type()
	fmt.Printf("type %s\n", t)
	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}
}

which will print

type *os.File
func (*os.File) Chdir() error
func (*os.File) Chmod(os.FileMode) error
func (*os.File) Chown(int, int) error
func (*os.File) Close() error
func (*os.File) Fd() uintptr
func (*os.File) Name() string
func (*os.File) Read([]uint8) (int, error)
func (*os.File) ReadAt([]uint8, int64) (int, error)
func (*os.File) ReadFrom(io.Reader) (int64, error)
func (*os.File) Readdir(int) ([]os.FileInfo, error)
func (*os.File) Readdirnames(int) ([]string, error)
func (*os.File) Seek(int64, int) (int64, error)
func (*os.File) SetDeadline(time.Time) error
func (*os.File) SetReadDeadline(time.Time) error
func (*os.File) SetWriteDeadline(time.Time) error
func (*os.File) Stat() (os.FileInfo, error)
func (*os.File) Sync() error
func (*os.File) SyscallConn() (syscall.RawConn, error)
func (*os.File) Truncate(int64) error
func (*os.File) Write([]uint8) (int, error)
func (*os.File) WriteAt([]uint8, int64) (int, error)
func (*os.File) WriteString(string) (int, error)

Go Playground os.File

Interfaces for io.Writer,io.Reader,io.Closer and io.ReadWriter.

 go doc -short io.Writer
type Writer interface {
	Write(p []byte) (n int, err error)
}

 go doc -short io.Reader
type Reader interface {
	Read(p []byte) (n int, err error)
}

 go doc -short io.Closer
type Closer interface {
	Close() error
}

 go doc -short io.ReadWriter
type ReadWriter interface {
	Reader
	Writer
}

Thus,an *os.File satisfies io.Reader, Writer, Closer, and ReadWriter.

On the other hand we can check what methods are supported by type *bytes.Buffer by triggering following code:

func main() {
	Print(bytes.NewBuffer([]byte{}))
}

func Print(x interface{}) {
	v := reflect.ValueOf(x)
	t := v.Type()
	fmt.Printf("type %s\n", t)
	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}
}

which will print

type *bytes.Buffer
func (*bytes.Buffer) Bytes() []uint8
func (*bytes.Buffer) Cap() int
func (*bytes.Buffer) Grow(int)
func (*bytes.Buffer) Len() int
func (*bytes.Buffer) Next(int) []uint8
func (*bytes.Buffer) Read([]uint8) (int, error)
func (*bytes.Buffer) ReadByte() (uint8, error)
func (*bytes.Buffer) ReadBytes(uint8) ([]uint8, error)
func (*bytes.Buffer) ReadFrom(io.Reader) (int64, error)
func (*bytes.Buffer) ReadRune() (int32, int, error)
func (*bytes.Buffer) ReadString(uint8) (string, error)
func (*bytes.Buffer) Reset()
func (*bytes.Buffer) String() string
func (*bytes.Buffer) Truncate(int)
func (*bytes.Buffer) UnreadByte() error
func (*bytes.Buffer) UnreadRune() error
func (*bytes.Buffer) Write([]uint8) (int, error)
func (*bytes.Buffer) WriteByte(uint8) error
func (*bytes.Buffer) WriteRune(int32) (int, error)
func (*bytes.Buffer) WriteString(string) (int, error)
func (*bytes.Buffer) WriteTo(io.Writer) (int64, error)

Thus, *bytes.Buffer satisfies Reader, Writer, and ReadWriter,but does not satisfy Closer because it does not have a Close method. As a shorthand, Go programmers often say that a concrete type is a particular interface type, meaning that it satisfies the interface. For example, a *bytes.Buffer is an io.Writer; an *os.File is an io.ReadWriter.

Go Playground bytes.Buffer

SORT

Sort package provides methods for sorting slices and user-defined collections.

Example for sorting slice of ints or strings.

func main() {
	xi := []int{1, 2, 4, 6, 2, 0, 2}
	xs := []string{"Mile", "Anci", "Pera", "Kica"}

	fmt.Println("unsorted:", xi)
	sort.Ints(xi) // 
	fmt.Println("sorted:", xi)

	fmt.Println("unsorted:", xs)
	sort.Strings(xs)
	fmt.Println("sorted:", xs)
}

Go Playground sort slices

Example for sorting user-defined collection.

type Person struct {
	Name string
	Age  int
}

type ByAge []Person

// by defining methods for ByAge type it also implements interface for sort.Interface
func (a ByAge) Len() int           { return len(a) }
func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }

func main() {
	p1 := Person{"Mile", 26}
	p2 := Person{"Mitar", 21}
	p3 := Person{"Mira", 32}
	p4 := Person{"Bora", 25}

	people := []Person{p1, p2, p3, p4}

	// listing methods for ByAge(people)
	// by defining methods for ByAge type it also implements interface for sort.Interface
	fmt.Println("List of methods provided by ByAge(people):")
	Print(ByAge(people))

	sort.Sort(ByAge(people))

	// alternative option to sort records in people based on Name field
	// desc order people[i].Age > people[j].Age
	// asc order people[i].Age < people[j].Age
	sort.Slice(people, func(i, j int) bool {
		return people[i].Name < people[j].Name
	})

	fmt.Println("people:",people)

}

func Print(x interface{}) {
	v := reflect.ValueOf(x)
	t := v.Type()
	fmt.Printf("type %s\n", t)
	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}
}

Go Playground sort custom

BCRYPT

This package implements bcrypt hashing method.

package main

import (
	"fmt"
	"golang.org/x/crypto/bcrypt"
)

func main() {
	s := "password123"
	// arguments []byte(s) and bcrypt.MinCost
	// generate encrypted data []byte as bs
	bs, err := bcrypt.GenerateFromPassword([]byte(s), bcrypt.MinCost)
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(s)
	fmt.Println(string(bs))

	s1 := "password1234"
	// comparing hash of previous encrypted data with provided []byte(s1)
	err = bcrypt.CompareHashAndPassword(bs, []byte(s1))
	fmt.Println(err)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(string(s))
}

Go Playground bcrypt

Concurrency

Concurrency is a design pattern or the way we write code.Concurrency is not guaranteed that your code will run in parallel.For that you need to have machine with multiple CPUs and your code have to use concurrency patterns.

Goroutines

When a program starts, its only goroutine is that one that calls the main function, so we call it the main goroutine.New goroutines are created by the go statement.Syntactically, a go statemnet is an ordinary function or method call prefixed by the keyword go.A go statement causes the function to be called in a newly created goroutine.Notice the difference:

f() // call f();wait for it to return
go f() // create a new goroutine that calls f();don't wait

WaitGroup

A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls method Add to set the number of goroutines to wait for. Then each of the goroutines runs and calls metdho Done when finished. At the same time, method Wait can be used to block until all goroutines have finished.

var wg sync.WaitGroup

func main() {
	fmt.Println("OS:\t\t", runtime.GOOS)
	fmt.Println("ARCH:\t\t", runtime.GOARCH)
	fmt.Println("CPU:\t\t", runtime.NumCPU())
	fmt.Println("Goroutine:\t", runtime.NumGoroutine())

	// Add adds delta, which may be negative, to the WaitGroup counter.
	wg.Add(1)
	
	// launch foo() in new goroutine
	go foo()
	bar()

	fmt.Println("CPU:\t\t", runtime.NumCPU())
	fmt.Println("Goroutine:\t", runtime.NumGoroutine())
	// Wait blocks until the WaitGroup counter is zero.
	wg.Wait()
	
	// when func main() is done it will shutdown all goroutines which where launched from it
}

func foo() {
	for i := 0; i < 10; i++ {
		fmt.Println("foo:", i)
	}
	// Done decrements the WaitGroup counter by one.
	wg.Done()
}

func bar() {
	for i := 0; i < 10; i++ {
		fmt.Println("bar:", i)
	}
}

Go Playground wait group

Race condition

When two or more goroutines have unsynchronized access to a shared resource and attempt to read and write to that resource at the same time, you have what’s called a race condition.Read and write operations against a shared resource must always be atomic, or in other words, done by only one goroutine at a time.

Use go run -race main.go to detect if there race condition occurs.

var x = 0

func myincrement() {
	x = x + 1
}

func main() {
	for i := 0; i < 1000; i++ {
		go myincrement()
	}
	time.Sleep(1 * time.Second)
	fmt.Println("final value of x is", x)
}

will output

▶ go run -race race.go 
==================
WARNING: DATA RACE
Read at 0x0000006469c0 by goroutine 8:
  main.myincrement()
      /home/mjv/go/src/learn/test/race.go:11 +0x3a

Previous write at 0x0000006469c0 by goroutine 7:
  main.myincrement()
      /home/mjv/go/src/learn/test/race.go:11 +0x56

Goroutine 8 (running) created at:
  main.main()
      /home/mjv/go/src/learn/test/race.go:16 +0x4f

Goroutine 7 (finished) created at:
  main.main()
      /home/mjv/go/src/learn/test/race.go:16 +0x4f
==================
final value of x is 3
Found 1 data race(s)
exit status 66

Go Playground race

Mutex

Mutex is used for avoiding race conditions which can happen in concurrency designed code.

mutex example

var x = 0
// adding variable for Mutex
var mu sync.Mutex
// adding variable for WaitGroup
var wg sync.WaitGroup

func myincrement() {
	// call Done when finish with work, decrement WaitGroup counter when goroutine completes
        defer wg.Done()
        mu.Lock() // locking mu
        x = x + 1
        mu.Unlock() // unlocking mu
}
func main() {
        
        for i := 0; i < 1000; i++ {
		// increment the WaitGroup counter
                wg.Add(1) 
                go myincrement()
        }
        time.Sleep(1 * time.Second)
	// wait for all goroutines to finish their work
        wg.Wait()
        fmt.Println("final value of x is", x)
}

Go Playground mutex

Atomic

Package atomic provides low-level atomic memory primitives useful for implementing synchronization algorithms.

Most common actions are:

  • atomic.AddT - secure way to add delta to some value with type T
  • atomic.LoadT - secure way to read some value with type T
  • atomic.StoreT - secure way to write to some value with type T

atomic example

// add variable for WaitGroup
var wg sync.WaitGroup
// add varible for atomic
var counter uint64

const gs = 1000

func myincrement(c *uint64) {
	// use AddUint64 for secure increasing.Only one goroutine can perform and complete this add operation at a time
	// when goroutines attempt to call any atomic function, they're automatically synchronized against the variable that's referenced
        atomic.AddUint64(c, 1)
	// LoadUint64 is used to read in a safe way
        fmt.Println("load:",atomic.LoadUint64(c))
	// Gosched yields the processor, allowing other goroutines to run. It does not
        // suspend the current goroutine, so execution resumes automatically.
        runtime.Gosched()
	wg.Done()
}
func main() {
	// set the number of goroutines to wait for
	wg.Add(gs)
	for i := 0; i < gs; i++ {
		go myincrement(&counter)
	}
	time.Sleep(1 * time.Second)
	// wait for all goroutines completes their work
	wg.Wait()
	fmt.Println("final value of c is", counter)

}

Go Playground atomic

Channels

A channel is a communication mechanism that lets one goroutine send values to another goroutine.Channels have to pass/receive the value at the same time.By default, sends and receives BLOCK until the other side is ready.Meaning, a receive will block only if there’s no value in the channel to receive and,a send will block only if there’s no available buffer to place the value being sent.This allows goroutines to synchronize without explicit locks or condition variables. As with maps, a channel is a reference to the data structure created by make.When we copy a channel or pass one as an argument to a function, we are copying a reference, so caller and callee refer to the same data structure. A send statement transmits a value from one goroutine,through the channel,to another goroutine executing a corresponding receiver expression.A send operatoin on an unbuffered channel blocks the sending goroutine until another goroutine executes a coresponding receive on the same channel, at which point the value is transmitted and both goroutiens may continue.Conversely, if the receive operation was attempted first, the receiving goroutine is blocked until another goroutine performs a send on the same channel.

In a send statment, the <- separates the cahnnel and value operands -> ch <- x In a receive expressionm <- precedes the channel operand -> x = <-ch

Buffered and unbuffered channels

unbuffered := make(chan int) // capacity is 0
buffered := make(chan int,2) // capacity is 2

If capacity is zero or absent,the channel is unbuffered and communication SUCCEEDS only when BOTH sender and receiver are ready.Otherwise,the channel is buffered and communications succeds without blocking if the buffer is not full (sends) or not empty (receives).A nil channel is NEVER ready for communication.

Go Playground channel

Directional channel Conversion from bidirectional to undirectional channel types are permitted in any assignment.

c := make(chan int) // bidirectional, send and receive
cs := make(chan <- int) // sends (outbound) only
cr := make(<- chan int) // receives (inbound) only

channels example

func main() {
	c := make(chan int)

	// send
	go foo(c)

	// receive
	// bar will block until there is a value to receive,in other words until foo completes
	bar(c)

	fmt.Println("terminating")
}

// declaring foo as send only channel parameter which can receive bidirectional channel
func foo(c chan<- int) {
	for i := 0; i < 10; i++ {
		fmt.Println("i:", i, "in foo")
	}
	c <- 42
}

// declaring bar as read only channel parameter which can receive bidirectional channel
func bar(c <-chan int) {
	fmt.Println(<-c)
}

Go Playground using channels

Close a channel A sender can close a channel to indicate that no more values will be sent.Closing is not needed, it is only necessary when the receiver must be told there are no more values coming, such as to terminate a range loop.

Range over channel The loop for i := range channel {} receives values from the channel repeatedly until it is closed.

close example

func main() {
	c := make(chan int)

	// send
	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		// close the channel so we can read from it with range
		// if we hadn't close the channel range c will wait for more values from the channel
		close(c)
	}()

	// range over a channel is pulling values from it until channel is closed
	for element := range c {
		fmt.Println(element)
	}

	fmt.Println("terminating")

}

Go Playground close and range

Select

Select statement waits until the communication (send or receive operation) is prepared for some cases to begin.It then performs that communication and execytes the case’s associated statements.The default statement in the select statement is used to protect select statement from blocking.This statement executes when there is no case statement is ready to proceed.

The select blocks until any of the case statements are ready
If one or more of the communications can proceed, a single one that can proceed is chosen via a uniform pseudo-random selection. Otherwise, if there is a default case, that case is chosen. If there is no default case, the “select” statement blocks until at least one of the communications can proceed.

select example

func main() {
	even := make(chan int)
	odd := make(chan int)
	quit := make(chan int)

	// send
	go send(even, odd, quit)

	// receive
	receive(even, odd, quit)

	fmt.Println("terminating")
}

func send(e, o, q chan<- int) {
	for i := 0; i < 100; i++ {
		if i%2 == 0 {
			e <- i
		} else {
			o <- i
		}
	}
	q <- 11

}

func receive(e, o, q <-chan int) {
	for {
		select {
		case v := <-e:
			fmt.Println("receive from even:", v)
		case v := <-o:
			fmt.Println("receive from odd:", v)
		case <-time.After(time.Second):
			fmt.Println("timeout")

		case v := <-q:
			fmt.Println("receive from quit:", v)
			// exit from func receive
			return
		}
	}
}

Go Playground select

Comma ok idiom

It is used to check if the channel is closed.After the last value has been received from a closed channel c, any receive from c will succeed without blocking, returning the zero value for the channel element. The form

x, ok := <-c

will also set ok to false for a closed channel.

comma ok idiom example

func main() {
	even := make(chan int)
	odd := make(chan int)
	quit := make(chan bool)

	// send
	go send(even, odd, quit)

	// receive
	receive(even, odd, quit)

	fmt.Println("terminating")
}

func send(even, odd chan<- int, quit chan<- bool) {
	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			even <- i
		} else {
			odd <- i
		}
	}
	close(quit)
}

func receive(even, odd <-chan int, quit <-chan bool) {
	for {
		select {
		case v := <-even:
			fmt.Println("receive from even:", v)
		case v := <-odd:
			fmt.Println("receive from odd:", v)
		case i, ok := <-quit:
			if !ok {
				fmt.Println("from comma ok", i, ok)
				// exit from func receive
				return
			} else {
				fmt.Println("from comma ok", i, ok)
			}
		}
	}
}

Go Playground comma ok

comma idiom example 2

func main() {
	c := make(chan int)

	go func() {
		c <- 42
		close(c)
	}()

	// it will receive a value as well
	v, ok := <-c
	fmt.Println(v, ok)
	
	// After the last value has been received from a closed
    	// channel c, any receive from c will succeed without blocking, returning the
    	// zero value for the channel element and ok will be set to false
	v, ok = <-c
	fmt.Println(v, ok)
}

Go Playground comma ok2

Fan-in

A function can read from multiple inputs and proceed until all are closed by multiplexing the input channels onto a single channel that’s closed when all the inputs are closed.

graph TB; A1[input] -->B1(channel) A2[input] -->B2(channel) A3[input] -->B3(channel) B1 --> C[Channel] B2 --> C[Channel] B3 --> C[Channel] C -->D[worker]

fan-in example

func main() {
	even := make(chan int) // bidirectional channel
	odd := make(chan int) // bidirectional channel
	fanin := make(chan int) // bidirectional channel

	// send
	go send(even, odd)

	// receive
	go receive(even, odd, fanin)

	for v := range fanin {
		fmt.Println(v)
	}

	fmt.Println("terminating")
}

// populate two outbound(send to) channels with data
func send(even, odd chan<- int) {

	for i := 0; i < 10; i++ {
		if i%2 == 0 {
			even <- i
		} else {
			odd <- i
		}
	}
	// close outbound channels when there are no data to add to it
	close(even) 
	close(odd)
}

// use two inbound channels and merge it to one outbound channel
func receive(even, odd <-chan int, fanin chan<- int) {
	var wg sync.WaitGroup
	wg.Add(2)

	go func() {

		for v := range even {
			fanin <- v
		}
		wg.Done()

	}()
	go func() {
		for v := range odd {
			fanin <- v
		}
		wg.Done()
	}()

	wg.Wait()
	close(fanin)
}

Go Playground fan-in

Fan-out

Multiple functions can read from the same channel until that channel is closed.

graph TB; A[input] -->B(channel) B -->C1[worker] B -->C2[worker] B -->C3[worker]
func main() {
	c1 := make(chan int)
	c2 := make(chan int)

	go populate(c1)

	go fanOutIn(c1, c2)

	for v := range c2 {
		fmt.Println(v)
	}

	fmt.Println("terminating")
}

// populate c as outbound channel
func populate(c chan<- int) {
	for i := 0; i < 10; i++ {
		c <- i
	}
	close(c)
}

// populate c2 as outbound channel with N number of workers which are using c1 as inbound channel
func fanOutIn(c1 <-chan int, c2 chan<- int) {
	var wg sync.WaitGroup

	for v := range c1 {
		wg.Add(1)
		go func(v2 int) {
			c2 <- timeConsumingWork(v2)
			wg.Done()
		}(v)
	}
	fmt.Println("goroutine:", runtime.NumGoroutine())
	wg.Wait()
	close(c2)
}

func timeConsumingWork(n int) int {
	time.Sleep(time.Microsecond * time.Duration(rand.Intn(500)))
	return n + rand.Intn(1000)
}

Go Playground fan-out

Context

This package is used for processes which are working with request-scoped data ( used IDs,auth tokens,session IDs,tracing IDs,…). Its purpose is to pass request-scope values, cancellation signals,deadline across API boundaries to all the goroutines involved in handling a request.

func main() {
	// context.Background() is parent context
	// ctx type is *context.cancelCtx, cancel type is context.CancelFunc
	ctx, cancel := context.WithCancel(context.Background())

	fmt.Println("error check 1:", ctx.Err())
	fmt.Println("goroutines check 1:", runtime.NumGoroutine())

	go func() {
		n := 0
		for {
			select {
			// when cancel() is called this func is terminated
			case <-ctx.Done():
				return
			default:
				n++
				time.Sleep(time.Millisecond * 200)
				fmt.Println("working:", n)
			}
		}
	}()

	time.Sleep(time.Second * 2)
	fmt.Println("error check 2:", ctx.Err())
	fmt.Println("goroutines check 2:", runtime.NumGoroutine())

	fmt.Println("about to cancel context")
	cancel()
	fmt.Println("cancelled context")

	time.Sleep(time.Second * 2)
	fmt.Println("error check 3:", ctx.Err())
	fmt.Println("goroutines check 3:", runtime.NumGoroutine())
}

Go Playground context

Errors

Following expressions can be used to log errors:

fmt.Println() // logging to stdout
log.Println() // logging to stdout including the date
log.Fatalln() // logging with Print() and calling os.Exit(1) afterwards
log.Panicln() // logging with Print() and calling panic() afterwards

Adding info to the errors

errors.New(string) // createing new type of error type
fmt.Errorf() // wrapper of errors.New(string)

Testing and benchmarking

Package testing provides support for automated testing of Go packages. It is intended to be used in concert with the “go test” command, whichautomates execution of any function.In short B is used for Benchmarking,E is used for Examples and T is used for Testing, or in short BET.

Testing

Tests must be in the same file that ends with *_test.go.It is recommended to put it the same package as the one being tested.Signature of the test is shown below:

// Function TestFoo is used to test func Foo
// Name of the function for testing must be declared as TestXxx where first X must be uppercase  
func TestFoo(t *testing.T) {
	// what we got by calling target function
	got := Foo("Mile")
	// what we want to get as an output from target function
	want := "Welcome from Foo: Mile"
	if got != want {
		t.Errorf("Expected: %v,got: %v\n", want, got)
	}
}

table test


// addSum is the function which sum elements of slice of int values
func addSum(xi ...int) int {
	total := 0
	for _, v := range xi {
		total += v
	}
	return total
}

// TestAddSum is testing addSum function
func TestAddSum(t *testing.T) {
	tests := []struct {
		list []int
		want int
	}{
		{list: []int{1, 1, 1}, want: 3},
		{list: []int{0, 1, 2}, want: 3},
		{list: []int{1, -1, 0, 1}, want: 1},
		{list: []int{1, 2, 3}, want: 6},
	}

	for _, tt := range tests {
		testname := fmt.Sprintf("%d -> %d", tt.list, tt.want)
		t.Run(testname, func(t *testing.T) {
			if got := addSum(tt.list...); !reflect.DeepEqual(tt.want, got) {
				t.Errorf("Expected: %v, got: %v", tt.want, got)
			}
		})
	}
}

Go Playground table test

Running test can be done via following commands:

go test -run=funcName // test against funcName
go test -v // verbose 

Coverage

One can check the code coverage of the test via:

go test cover // It is considered as perfect if the coverage value is above 70%. 
// create coverprofile's output as c.out
go test -coverprofile c.out
// open c.out in the browser
go tool cover c.out
// print coverage per functions
go tool cover -func=c.out
// write c.out to html file coverage.html
go tool cover -html=c.out -o coverage.html

Examples

Same rules applies for the examples as for the test functions.These functions will be visible on documentation page of tha package.

// Function ExampleFoo is used to show how to use func Foo.This will be printed on the documentation page of the package and related func.
// Name of the function used as example must be declared as ExampleXxx where first X must be uppercase  
func ExampleFoo() {
	fmt.Println(Foo("Mile"))
	// Output:
	// Welcome from Foo: Mile
}

Bencmarking

It is used to measure the performance of the code.

// Function BenchmarkFoo is used to benchmark function Foo
// Name of the function for benchmarking must be declared as BenchmarkXxx where first X must be uppercase  
// The benchmark function must run the target code b.N times.
func BenchmarkFoo(b *testing.B) {
	for i := 0; i < b.N; i++ {
		Foo("Mile")
	}
}

Running benchmark tests against some code is done via:

go test -bench . // all functions
go test -bench=funcName // specific function
go test -bench . -v -count N // verbose (-v) plus call benchmark for N times (count)

The output of a bencmark looks like:

▶ go test -bench .
goos: linux
goarch: amd64
pkg: learn/benchmark/foo
BenchmarkFoo-4   	 7853067	       261 ns/op
PASS
ok  	learn/benchmark/foo	2.213s

which means that loop runs 7853067 times at speed of 261ns per loop.

Go Playground BET complete