What is gobook?

Gobok started as an extension to run Go interactivly in VS Code and save results to markdown ready for uploading to a blog or documentation sites. This site is built in combination with gobook and a Rust static site generator, it's thrown together with personal notes I took as I was learning Go, but over time will be refined to something similar to the Rust Book


books := []string{"gobook", "mdbook"}
fmt.Printf("This site was built with %s and %s", books[0], books[1]) 
This site was built with gobook and mdbook

extension

Installing gobook

This will allow you to start playing with go in the fastest way possible

Install software

  1. Download Go here
  2. Download VS Code here
  3. Download the Extension here

Test extension

  1. Create a new file called test.md

new_file

  1. If the extension doesn't active right click test.md and select reopen with, then Gobook:

reopen

reopen_with

  1. Add a code cell

add_cell

  1. Run some code

this_is_gobook

  1. ctrl + click the file name in terminal to view generated code

control_click

generated

  1. Upload test.md to Github for a niceley rendered markdown document, with outputs retained

github

Basics

Variables

Declare and initialize


var a int = 10

Inferred type


var b = 10
fmt.Println(reflect.TypeOf(b))
int

Initialized to 0


var c int
fmt.Println(c)
0

Multi initializer


var d, e int = 10, 20

Inferred multi


var f, g = 10, "wow"
fmt.Printf("%v\n%v",reflect.TypeOf(f), reflect.TypeOf(g))
int
string

Multiline initializer


var (
	h    int
	i        = 20
	j    int = 30
	k, l     = 40, "hello"
	m, n string
)
fmt.Println(h, i, j, k, l, m, n)
0 20 30 40 hello

Shorthand initializer


o := 10
p, q := 30, "hello"
fmt.Println(o, p, q)
10 30 hello

Const declaration


// Like C this swaps out the consts with literals during compile time
const (
	idKey   string = "id"
	nameKey        = "name"
)
fmt.Println(idKey, nameKey)
id name

Arrays

Initialize array to 0 values


a := [3]int{}
fmt.Println(a)
[0 0 0]

Set to width of provided literal


b := [...]int{5 + 5, 20, 30}
fmt.Println(b)
[10 20 30]

Compare arrays


// Arrays can be compared in go, but Slices cannot
println(a == b)
false

Set by index, blank are initialized to 0


d := [6]int{1, 2: 4, 5, 5: 100}
fmt.Println(d)
[1 0 4 5 0 100]

Multidimensional array


e := [2][4]int{}
fmt.Println(e)
[[0 0 0 0] [0 0 0 0]]

Go has a simple multidimensional array implementation which incurs performance penalities

Slices

Create from an array


array := [4]int{1,2,3,4}
slice := array[:3]
fmt.Println("array:", array)
fmt.Println("slice:", slice)
array: [1 2 3 4]
slice: [1 2 3]

Shorthand initializer


a := []int{10, 0, 0}
[10 0 0]

Append two slices together


c := []int{10, 20, 30}
d := []int{1, 2: 3, 4, 5: 10}
e := append(c, d...)
fmt.Println(e)
[10 20 30 1 0 3 4 0 10]

Initialize with make


f := make([]int, 5)
fmt.Printf("%v len: %v cap %v\n", f, len(f), cap(f))
[0 0 0 0 0] len: 5 cap 5

Make with capacity


g := make([]int, 5, 32)
fmt.Printf("%v len: %v cap %v", g, len(g), cap(g))
fmt.Println()
[0 0 0 0 0] len: 5 cap 32

This sets asside 32 ints in memory to save the runtime doing extra work moving things around in memory, appending to a slice would otherwise have to create a new slice.

Copying Slices

Taking a slice of another slice or array uses a pointer, so modifying or appending items will also effect the original. See below examples on how to avoid this

Once the new slice grows beyond the max it creates a new copy, now any modifications to any value won't effect the original


a := []int{0, 1, 2}
b := a

b = append(b, 30)
b[2] = 60

fmt.Printf("a: %v len: %v cap: %v\n", a, len(a), cap(a))
fmt.Printf("b: %v len: %v cap: %v\n", b, len(b), cap(b))
a: [0 1 2] len: 3 cap: 3
b: [0 1 60 30] len: 4 cap: 6

If the new slice didn't grow beyond the original, modifications will still effect the original


c := []int{0, 1, 2}
d := c

d[2] = 60

fmt.Printf("a: %v len: %v cap: %v\n", c, len(c), cap(c))
fmt.Printf("b: %v len: %v cap: %v\n", d, len(d), cap(d))
a: [0 1 60] len: 3 cap: 3
b: [0 1 60] len: 3 cap: 3

Copy to stop slice modifications effecting original


e := []int{1, 2, 3, 4}
f := make([]int, 2)

copy(f, e[2:])
f[0] = 20

fmt.Printf("a: %v\n", e)
fmt.Printf("b: %v\n", f)
a: [1 2 3 4]
b: [20 4]

Array to slice copy


g := [5]int{1, 2, 3, 4, 5}
h := []int{99, 98, 97}
elementsCopied := copy(h[1:], g[3:])
fmt.Println("elements copied:",elementsCopied,"\ne:",g,"\nf:",h)
elements copied: 2 
e: [1 2 3 4 5] 
f: [99 4 5]

Looping

Go has a single loop statement: for

Standard for statement


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

Condition only (while loop)


i := 1
for i < 100 {
	fmt.Println(i)
	i = i * 2
}
1
2
4
8
16
32
64

Infinite Loop


x := 0
for {
	x++
	fmt.Println(x)
	if x == 5 {
		break
	}
}
1
2
3
4
5

Looping on

A for loop of a string converts UTF-8 to 32bit, so non standard characters are still treated as one


m := "moon🌜y"
for i, v := range m {
	fmt.Printf("%v %8v %-6v\n", i, v, string(v))
}
0      109 m     
1      111 o     
2      111 o     
3      110 n     
4   127772 🌜     
8      121 y

Countinue to label


	samples := []string{"hello", "apple_Ο€!"}
outer:
	for _, sample := range samples {
		for i, r := range sample {
			fmt.Println(i, r, string(r))
			if r == 'l' {
				continue outer
			}
		}
		fmt.Println("Outer")
	}
0 104 h
1 101 e
2 108 l
0 97 a
1 112 p
2 112 p
3 108 l

Goto


func gotoer() {
	goto done
	for {
			fmt.Println("Skip the infinite loop")
	}
done:
	fmt.Println("Done")
}


gotoer()
Done

Notes

  • The value returned in a for-range loop is a copy, not a reference

Maps

Assigning to an unitialized map causes a panic


	var a map[string]int
	a["testKey"] = 1
panic: assignment to entry in nil map

goroutine 1 [running]:
main.main()
	/tmp/main.go:7 +0x73
exit status 2

Initialize an empty map and add keys


totalWins := map[string]int{}
totalWins["lakers"] = 20
totalWins["suns"]++
fmt.Println(totalWins)
map[lakers:20 suns:1]

Initialize with map literals


x := map[int]string{
	10: "wow cool",
	40: "I like it",
}
fmt.Println(x)
map[10:wow cool 40:I like it]

Multi level map literal


	teams := map[string][]string{
		"Orcas":   {"Fred", "Ralph", "Bijou"},
		"Lions":   {"Sarah", "Peter", "Billie"},
		"Kittens": {"Waldo", "Raul", "Ze"},
	}
	fmt.Println(teams)
map[Kittens:[Waldo Raul Ze] Lions:[Sarah Peter Billie] Orcas:[Fred Ralph Bijou]]

Check if the key exists and print it if it does


v, ok := teams["Orcas"]
if(ok) { 
	fmt.Println(v)
}
[Fred Ralph Bijou]

Delete a key


delete(teams, "Orcas")
fmt.Println(teams)
map[Kittens:[Waldo Raul Ze] Lions:[Sarah Peter Billie]]

Notes

  • Map keys can be any comparable type

Sets

Using a map as a set

Map keys don't duplicate


intSet := map[int]bool{}
vals := []int{1, 1, 2, 2, 3}
for _, v := range vals { 
	intSet[v] = true
}
fmt.Println(intSet)
map[1:true 2:true 3:true]

Trying to retrieve a key that doesn't exist gives false


fmt.Println(intSet[5])
false

Use a struct as a set


structSet := map[int]struct{}{}
var exists = struct{}{}
ints := []int{1, 1, 2, 2, 3}
for _, v := range ints {
	structSet[v] = exists
}

for i, v := range structSet {
	fmt.Println(i, v)
}

if _, ok := structSet[4]; ok {
	fmt.Println("number is in the set")
} else {
	fmt.Println("not in the set")
}
1 {}
2 {}
3 {}
not in the set

Make a set type


var exists = struct{}{}
type set struct {
	m map[string]struct{}
}

func NewSet() *set {
	s := &set{}
	s.m = make(map[string]struct{})
	return s
}

func (s *set) Add(value string) {
	s.m[value] = exists
}

func (s *set) Remove(value string) {
	delete(s.m, value)
}

func (s *set) Contains(value string) bool {
	_, c := s.m[value]
	return c
}


s := NewSet()
s.Add("Peter")
s.Add("David")
fmt.Println(s.Contains("Peter"))  // True
fmt.Println(s.Contains("George")) // False
s.Remove("David")
fmt.Println(s.Contains("David")) // False
true
false
false

Switch

Standard Switch


words := []string{"a", "cow", "smile", "gopher",
	"octopus", "anthropologist"}
for _, word := range words {
	switch size := len(word); size {
		case 1, 2, 3, 4:
			fmt.Println(word, "is a short word!")
		case 5:
			wordLen := len(word)
			fmt.Println(word, "is exactly the right length:", wordLen)
		case 6, 7, 8, 9:
		default:
			fmt.Println(word, "is a long word!")
	}
}
a is a short word!
cow is a short word!
smile is exactly the right length: 5
anthropologist is a long word!

Break out of a switch inside a loop


func looper() {
loop:
	for i := 0; i < 10; i++ {
		switch {
		case i%2 == 0:
			fmt.Println(i, "is even")
		case i%3 == 0:
			fmt.Println(i, "is divisible by 3 but not 2")
		case i%7 == 0:
			fmt.Println("exit the loop!")
			break loop
		default:
			fmt.Println(i, "is boring")
		}
	}
}


looper()
0 is even
1 is boring
2 is even
3 is divisible by 3 but not 2
4 is even
5 is boring
6 is even
exit the loop!

Blank Switch


newWords := []string{"hi", "salutations", "hello"}
for _, word := range newWords {
	switch wordLen := len(word); {
		case wordLen < 5:
			fmt.Println(word, "is a short word!")
		case wordLen > 10:
			fmt.Println(word, "is a long word!")
		default:
			fmt.Println(word, "is exactly the right length.")
	}
}
hi is a short word!
salutations is a long word!
hello is exactly the right length.


switch n := rand.Intn(10); {
	case n == 0:
		fmt.Println("That's too low")
	case n > 5:
		fmt.Println("That's bigger")
	default:
		fmt.Println("Cool one")
}
Cool one

Strings

Converting string to runes retains emoji's as a single item


s := "Hello, 🌞"
bs := []byte(s)
rs := []rune(s)
fmt.Println("Bytes:", bs)
fmt.Println("Runes:", rs)
Bytes: [72 101 108 108 111 44 32 240 159 140 158]
Runes: [72 101 108 108 111 44 32 127774]

Converting back to string from either still formats correctly


bytes := string(bs)
runes := string(rs)
fmt.Println("Bytes:",bytes)
fmt.Println("Runes:",runes)
Bytes: Hello, 🌞
Runes: Hello, 🌞

String slice


h := "Hello there"
for _, v := range(h[6:9]) {
	fmt.Printf("%c", v)
}
the

Converting an int to a sting with give the UTF-8 representation


x := 50
fmt.Println(string(x))
xString := fmt.Sprintf("%v", x)
fmt.Println(xString)
2
50

Functional

Basics

Simulate optional parameters


type MyFuncOpts struct {
	FirstName string
	LastName string
	Age int
}


func printNameAge(opts MyFuncOpts) error {
	if opts.LastName != "" {
		fmt.Println(opts.LastName)
	}
	if opts.Age != 0 {
		fmt.Println("Age =", opts.Age)
		} else { 
			fmt.Println("No age provided")
	}
	return nil
}


func test() {
	printNameAge(MyFuncOpts {
		LastName: "Clayton",
	})
}


test()
Clayton
No age provided

Variadic input parameters and slices


func addTo(base int, values ...int) []int {
	out := make([]int, 0, len(values))
	for _, v := range values {
		out = append(out, base+v)
	}
	return out
}

Standard call


sum := addTo(29,123,14,13)
fmt.Println(sum)
[152 43 42]

Spread an array into the parameters


a := []int{20,30,40}
sumSpread := addTo(10, a...)
fmt.Println(sumSpread)
[30 40 50]

Named return values


func divAndRemainder(numerator int, denominator int) (result int, remainder int, err error) {
	if denominator == 0 {
		err = errors.New("Cannot divide by zero")
		return result, remainder, err
	}
	result, remainder = numerator/denominator, numerator%denominator
	return result, remainder, err
}


x, y, err := divAndRemainder(5, 0)
if err != nil {
	fmt.Println("Oh no an error:", err)
	os.Exit(1)
}
fmt.Println(x, y, err)
No age provided
[152 43 42]
[30 40 50]
gobook-output-start
Oh no an error: Cannot divide by zero
exit status 1

Blank return (Go community says this is bad idea as it's unclear!)


func divAndRemainder(numerator, denominator int) (result int, remainder int, err error) {
	if denominator == 0 {
		return 0, 0, errors.New("Cannot divide by zero")
	}
	result, remainder = numerator/denominator, numerator%denominator
	return
}
/tmp/main.go:45:6: divAndRemainder redeclared in this block
	previous declaration at /tmp/main.go:37:86


x, y, z := divAndRemainder(10, 2)
fmt.Println(x, y, z)
5 0 <nil>

Anonymous function


	for i := 0; i < 3; i++ {
		x := func(j int) int {
			fmt.Println("Printing", j, "from inside of an anonymous function")
			return i
		}(i)
		fmt.Println("Printing", x, "from the value returned")
	}
Printing 0 from inside of an anonymous function
Printing 0 from the value returned
Printing 1 from inside of an anonymous function
Printing 1 from the value returned
Printing 2 from inside of an anonymous function
Printing 2 from the value returned

First Class Functions


func add(i int, j int) int { return i + j }
func sub(i int, j int) int { return i - j }
func mul(i int, j int) int { return i * j }
func div(i int, j int) int { return i / j }


// Documentation for type
type opFuncType func(int, int) int

var opMap = map[string]opFuncType{
	"+": add,
	"-": sub,
	"*": mul,
	"/": div,
}
/tmp/main.go:74:6: opMap declared but not used


func run() {
	expressions := [][]string{
		{"2", "-", "3"},
		{"2", "+", "3"},
		{"2", "*", "3"},
		{"2", "/", "3"},
		{"2", "%", "3"},
		{"two", "plus", "three"},
		{"5"},
	}
	for _, expression := range expressions {
		if len(expression) != 3 {
			fmt.Println("invalid expression", expression, "(needs three parameters)")
			continue
		}
		p1, err := strconv.Atoi(expression[0])
		if err != nil {
			fmt.Println(err)
			continue
		}
		op := expression[1]
		opFunc, ok := opMap[op]
		if !ok {
			fmt.Println("unsupported operator:", op)
			continue
		}
		p2, err := strconv.Atoi(expression[2])
		if err != nil {
			fmt.Println(err)
			continue
		}
		result := opFunc(p1, p2)
		fmt.Println(result)
	}
}


run()
-1
5
6
0
unsupported operator: %
strconv.Atoi: parsing "two": invalid syntax
invalid expression [5] (needs three parameters)

Shadowing

Shadow variable x


func block() {
x := 10
if x > 5 {
	fmt.Println("Inner block", x)
	x:= 5 
	fmt.Println("Reinit inner block:", x)
	}
	fmt.Println("Outer block", x)
}


block()
Inner block 10
Reinit inner block: 5
Outer block 10

Universe block

Redeclaring a variable in the universe block isn't detected by any linters or the shadow tool, be careful not to shadow anything in the universe block.


fmt.Println(true)
true := 10
fmt.Println(true)
true
10

Initializer in if/else statement scope


if n := rand.Intn(10); n == 0 {
	fmt.Println("That's too low")
} else if n > 5 {
	fmt.Println("That's too big:", n)
} else {
	fmt.Println("That's a good number:", n)
}
That's a good number: 1

Notes

Scope

  • Outside any function = package block
  • Imports = file block
  • Inside function / parameters = block
  • Anything inside {} is a another block

Access

  • Can access any outer scope identifiers

Call by value

Like C, Go is pass by value


type person struct{
	age int
	name string
}

func changeValues(i int, s string, p person) {
	i = i * 2
	s = "Changed string"
	p.age = 600
	p.name = "Changed Name"
}

Modifying the values passed in does not effect the values in the outer scope


p := person{20, "Original Name"}
i := 2
s := "Original string"

changeValues(i, s, p)
fmt.Println(i, s, p)
2 Original string {20 Original Name}

Modifying a map does effect the original, as it's a pointer


func modMap(m map[int]string){
	m[0] = "Changed value"
	m[1] = "This value will be deleted"
	m[2] = "Added value"
	delete(m, 1)
}


m := map[int]string{
	0: "first",
	1: "second",
}
modMap(m)
fmt.Println(m)
map[0:Changed value 2:Added value]

Modifying a slice will only effect items that have already been initialized, can't add new items.


func modSlice(s []int) {
	for k, v := range s {
		s[k] = v * 2
	}
	s = append(s, 10)
}


sl := []int{1,2,3}
modSlice(sl)
fmt.Println(sl)
[2 4 6]

Closures

Define the Person struct


type Person struct {
	FirstName string
	LastName  string
	Age       int
}

  • initialize a slice of 3 people
  • Sort by age, then last name
  • People is captured by the anonymous function closure,

people := []Person{
	{"Pat", "Patterson", 16},
	{"Amy", "Bobbart", 19},
	{"Bob", "Bobbart", 18},
}

sort.Slice(people, func(i int, j int) bool {
		return people[i].Age < people[j].Age
})

sort.Slice(people, func(i int, j int) bool {
		return people[i].LastName < people[j].LastName
})
fmt.Println(people)
[{Bob Bobbart 18} {Amy Bobbart 19} {Pat Patterson 16}]

Defining a closure type


// Returns a function that multiplies by the given factor
type twoFactor func(factor int) int


func makeMult(base int) twoFactor {
	return func(factor int) int {
		return base *factor
	}
}


twoBase := makeMult(2)
threeBase := makeMult(3)
fourBase := makeMult(4)
for i := 0; i < 10; i++ {
	fmt.Println(twoBase(i), threeBase(i), fourBase(i))
}
0 0 0
2 3 4
4 6 8
6 9 12
8 12 16
10 15 20
12 18 24
14 21 28
16 24 32
18 27 36

Remind caller to do something with a closure e.g. close a file, as it's returned to caller and Go doesn't allow unused values, they know that they should do something with it


func getFile(name string) (*os.File, func(), error) {
	file, err := os.Open(name)
	if err != nil {
		return nil, nil, err
	}
	return file, func() {
		file.Close()
	}, err
}


_, closer, err := getFile("./out.txt")
if err != nil {
	log.Fatal(err)
}
defer closer()
2 3 4
4 6 8
6 9 12
8 12 16
10 15 20
12 18 24
14 21 28
16 24 32
18 27 36
gobook-output-start
2021/08/18 10:27:47 open ./out.txt: no such file or directory
exit status 1

Types

import "fmt"

Types

Terms

  • Abstract Type - What a type should do, but not how it's done
  • Concrete Type - Specifies what and how i.e how to store data and implement method
  • Reciever - The type the method is being attached to, conventionally shorthand abbreviation of the types name

Declarations

Primitive


type Score int

Function


type Converter func(string)Score

Compound


type TeamScores map[string]Score

Struct

Methods


type Person struct {
	FirstName string
	LastName string
	Age int
}


func (p Person) String() string {
	return fmt.Sprintf("%s %s, age %d", p.FirstName, p.LastName, p.Age)
}


p := Person{
	"Jacky",
	"C",
	33,
}
p.String()

Assigning a method value and calling it


type Adder struct {
	start int
}


func (a *Adder) AddTo(val int) int {
	return a.start + val
}


myAdder := Adder{40}
f := myAdder.AddTo
myAdder.start = f(20)
fmt.Println(f(20))

Method Expression


f2 := Adder.AddTo
fmt.Println(f2(myAdder, 15))


// assigning untyped constants is valid
var i int = 300
var s Score = 100
var hs HighScore = 200 
hs = s // compilation error!
s = i // compilation error!
s = Score(i) // ok
hs = HighScore(s) // ok

iota type (enumerations)


type MailCategory int


const (
			Uncategorized MailCategory =  iota
			Personal
			Spam
			Social
			Advertisements
)


fmt.Println(Spam)
2


x := Personal
if x == Advertisements{
	fmt.Println("wow")
}
wow


import "fmt"

Terms

  • Interface: list the methods that must be implemented by a concrete type
  • Method set: the methods defined on an interface

Example of using a interface


type GoodDog interface {
	roll()
	pat()
}

type BadDog interface {
	bark()
	howl()
	GoodDog
}

type Dog struct {
	color string
	size  int
}

type Wolf struct {
	Dog
}

func (Wolf) bark() {
	fmt.Println("Woof!")
}
func (Wolf) howl() {
	fmt.Println("Awwwwwoooooooooo!")
}

func (Dog) roll() {
	fmt.Println("He rolls over")
}

func (Dog) pat() {
	fmt.Println("He likes that!")
}

func main() {
	hendrix := Dog{"blonde", 2}
	wolfy := Wolf{Dog{"black", 10}}
	var hendrix_interface GoodDog = hendrix
	var wolfy_interface BadDog = wolfy

	hendrix_interface.pat()
	wolfy_interface.bark()
	wolfy_interface.roll()
}

Interfaces are implemented as two pointers, value and type. Having a type assigned makes it non-nil


var s *string


fmt.Println(s == nil)


var i interface{}


fmt.Println(i == nil)


i = s


fmt.Println(i == nil)

Empty interface, can represent any data type


data := map[string]interface{}{}


import "fmt"

Define a struct


type person struct {
	name string
	age int
}


fred := person {
	"Fred",
	20,
}
fmt.Println(fred)

Anonymous struct


var person struct {
	name string
	age int
	pet string 
}


person.name = "bob"
person.age = 50
person.pet = "dog"
fmt.Println(person)

Shorthand


pet := struct {
	name string
	kind string
}{
	name: "Fido",
	kind: "dog",
}
fmt.Println(pet)

Notes

Initialization

  • No difference between assigning empty struct literal and not assigning a value, both initialize all fields to 0

Conversions

  • Structs can be type converted if fields are in the same order, with same naming and types

Comparisons

  • Structs are comparable if of the same type, and no field contains a slice, map, channel or function
  • Anonymous structs can be compared to a typed struct if contains same order, names and types

type firstPerson struct {
	name string
	age int
}


f := firstPerson{
	name: "Bob",
	age: 50,
}

var g struct {
	name string
	age int
}

g = f
fmt.Println(g == f)

Type Assertion


---
```go
import (
	"log"
	"fmt"
)

type conversion: change type assertion: reveal

Conversion example


type MyInt int


func main() {
	var i MyInt = 20
	i2 := int(i)
	fmt.Println(i2)
}
20

Assertion example (happens at runtime), always check type assertions!


func main() {
	var i interface{}
	var mine MyInt = 20
	i = mine
	i2, ok := i.(MyInt)
	if !ok {
		log.Fatalf("unexpected type for %v", i)
	}
	fmt.Println(i2)
}
20

Type switch to check type and do appropriate action i is shadowing here, one of few examples where shadowing is idiomatic


func doThings(i interface{}) {
	switch i := i.(type) {
	case nil:
		fmt.Println(i, "is of type interface{}")
	case int:
		fmt.Println(i, "is of type int")
	case MyInt:
		fmt.Println(i, "is of type MyInt")
	case string:
		fmt.Println(i, "is of type string")

	default:
		fmt.Println("I don't know what type this value is:", j)
	}
}

func main() {
	var j MyInt = 10
	doThings(j)
}

Example in the standard library using type assertion from the standard librar


// copyBuffer is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
	// If the reader has a WriteTo method, use it to do the copy.
	// Avoids an allocation and a copy.
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	// Similarly, if the writer has a ReadFrom method, use it to do the copy.
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// function continues...
}

Example of using fallback code when implementing a newer version of an API This comes from database/sql/driver in the standard library


func ctxDriverStmtExec(ctx context.Context, si driver.Stmt,
	nvdargs []driver.NamedValue) (driver.Result, error) {
if siCtx, is := si.(driver.StmtExecContext); is {
	return siCtx.ExecContext(ctx, nvdargs)
}
// fallback code is here
}




---

import "fmt"

type Employee struct {
	Name string
	ID   string
}

func (e Employee) Descriptions() string {
	return fmt.Sprintf("%s (%s)", e.Name, e.ID)
}

// Anything from Employee is promoted so Manager has direct access to it
type Manager struct {
	Employee
	Reports []Employee
}

func (m Manager) FindNewEmployees() []Employee {
	result := []Employee{}
	result = append(result, Employee{"Billy", "14560456"})
	result = append(result, Employee{"Donkey", "4662456"})
	return result
}

func main() {
	bob := Employee{"Bob", "42248465"}
	fred := Employee{"Fred", "45224865"}

	bossMan := Manager{bob, []Employee{fred}}

	bossMan.Reports = append(bossMan.Reports, bossMan.FindNewEmployees()...)

	fmt.Println("Boss name: ", bossMan.Name)
	fmt.Println("Boss id: ", bossMan.ID)
	fmt.Println("Boss reports:\n")
	for _, v := range bossMan.Reports {
		fmt.Printf("Name: %v\nid: %v\n\n", v.Name, v.ID)
	}
}

Boss name:  Bob
Boss id:  42248465
Boss reports:

Name: Fred
id: 45224865

Name: Billy
id: 14560456

Name: Donkey
id: 4662456


Contexts

context.WithValue

Simple example of passing around a value through a context

func main() {
	ctx := context.Background()
	ctx = addValue(ctx)
	readValue(ctx)
}

func addValue(ctx context.Context) context.Context {
	return context.WithValue(ctx, "key", "test-value")
}

func readValue(ctx context.Context) {
	val := ctx.Value("key")
	fmt.Println(val)
}

Example wrapping contexts

package main

import (
	"context"
	"fmt"
)

func main() {
	ctx := context.Background()
	ctx = addValue(ctx)
	fmt.Println(ctx.Value("first"))
}

func addValue(ctx context.Context) context.Context {
	a := context.WithValue(ctx, "first", "first-v")
	b := context.WithValue(a, "second", "second-v")
	c := context.WithValue(b, "third", "third-v")
	return c
}

Each Context is nested, but you can still grab the value at the bottom level

Example passing UUID using middleware

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/google/uuid"
	"github.com/gorilla/mux"
)

func main() {
	router := mux.NewRouter()
	router.Use(guidMiddleware)
	router.HandleFunc("/ishealthy", handleIsHealthy).Methods(http.MethodGet)
	http.ListenAndServe(":8080", router)
}

func handleIsHealthy(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	uuid := r.Context().Value("uuid")
	log.Printf("[%v] Returning 200 - Healthy", uuid)
	w.Write([]byte("Healthy"))
}

func guidMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		uuid := uuid.New()
		r = r.WithContext(context.WithValue(r.Context(), "uuid", uuid))
		next.ServeHTTP(w, r)
	})
}

Tooling

Cmds

Upgrade version of module

go get -u=patch github.com/[user]/[repo]

List variables

go env

See where the overwrite file is for GO

go env GOENV

Test Coverage

go test -v -coverpkg=./... -coverprofile=profile.cov ./... && go tool cover -func profile.cov

Imports sorter

go install golang.org/x/tools/cmd/goimports@latest

goimports -l -w .

The -l flag tells goimports to print the files with incorrect formatting to the console. The -w flag tells goimports to modify the files in-place. The . specifies the files to be scanned: everything in the current directory and all of its subdirectories.

Go Lint

go install golang.org/x/lint/golint@latest

# run it with:
golint ./...

Go vet

Passing wrong numbers of parameters, assigning values that are never used etc

go vet ./...

golangci-lint

Many different tools run over the same directory

golangci-lint run

go generate

Run a command from comments e.g. in a file:

//go:generate mockgen -destination=mocks/mock_foo.go -package=mocks . Foo

the run

go generate

shadow detection

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow@latest

makefile to fmt, lint, vet and build

.DEFAULT_GOAL := build

fmt:
	go fmt main.go
.PHONY:fmt
lint: fmt
	golint main.go
.PHONY:lint
vet: fmt
	go vet main.go
.PHONY:vet
build: vet
	go build main.go
.PHONY:build

Use a different version of go

go get golang.org/dl/go1.15.6
go1.15.6 download

Delete new version of go

go1.15.6 env GOROOT
rm -rf $(go1.15.6 env GOROOT)
rm $(go env GOPATH)/bin/go1.15.6
.DEFAULT_GOAL := build
fmt:
	go fmt main.go
.PHONY:fmt
lint: fmt
	golint main.go
.PHONY:lint
vet: fmt
	go vet main.go
	shadow main.go
.PHONY:vet
build: vet
	go build main.go
.PHONY:build

Keep your code in GOPATH which is typically ~/go/src/ Make a subdirectory for the extra package you want:



β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
β”œβ”€β”€ main.go
└── mypackage
    └── mypackage.go

In mypackage (make sure to capitalize anything you want to export):

package mypackage

func Double(a int) int {
	return a * 2
}

Now in main.go you can import the package:


package main

import (
	"fmt"

	"github.com/[user]/[repo]/package_example/mypackage"
)

func main() {
	num := mypackage.Double(2)
	output := print.Format(num)

	fmt.Println(output)
}

run:

go mod init
go mod tidy

When you upload this to github, others will be able to import mypackage via the same import line

github.com/pkg/profile

Start a cpu profile, save it to working directory

defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()

Standard Library


import(
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)


client := &http.Client{
	Timeout: 30 * time.Second,
}

req, err := http.NewRequestWithContext(context.Background(),
	http.MethodGet, "https://jsonplaceholder.typicode.com/todos/4", nil)
if err != nil {
	panic(err)
}

req.Header.Add("X-My-Client", "Learning Go")
res, err := client.Do(req)
if err != nil {
	panic(err)
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
	panic(fmt.Sprintf("unexpected status: got %v", res.Status))
}

fmt.Println(res.Header.Get("Content-Type"))

var data struct {
	UserID    int    `json:"userId"`
	ID        int    `json:"id"`
	Title     string `json:"title"`
	Completed bool   `json:"completed"`
}

err = json.NewDecoder(res.Body).Decode(&data)
if err != nil {
	panic(err)
}

fmt.Printf("%+v\n", data)
application/json; charset=utf-8
{UserID:1 ID:4 Title:et porro tempora Completed:true}


import "fmt"


func logOutput(message string) {
	fmt.Println(message)
}


type SimpleDataStore struct {
	userData map[string]string
}


func (sds SimpleDataStore) UserNameForID(userID string) (string, bool) {
	name, ok := sds.userData[userID]
	return name, ok
}
jack


SimpleDataStore:
func NewSimpleDataStore() SimpleDataStore {
return SimpleDataStore{
userData: map[string]string{
"1": "Fred",
"2": "Mary",


import (
	"compress/gzip"
	"fmt"
	"io"
	"log"
	"os"
)

Standard read file through buffer


file, err := os.Open("./out.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	data := make([]byte, 100)
	for {
		count, err := file.Read(data)
		if err != nil {
			if err == io.EOF {
				return
			}
			log.Fatal(err)
		}
		if count == 0 {
			return
		}
		fmt.Print(string(data[:count]))
	}

Read through zip and use buffer for more operations


func countLetters(r io.Reader) (map[string]int, error) {
	buf := make([]byte, 2048)
	out := map[string]int{}
	for {
		n, err := r.Read(buf)
		for _, b := range buf[:n] {
			if (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z') {
				out[string(b)]++
			}
		}
		if err == io.EOF {
			return out, nil
		}
		if err != nil {
			return nil, err
		}
	}
}


func buildGZipReader(fileName string) (*gzip.Reader, func(), error) {
	r, err := os.Open(fileName)
	if err != nil {
		return nil, nil, err
	}
	gr, err := gzip.NewReader(r)
	if err != nil {
		return nil, nil, err
	}
	return gr, func() {
		gr.Close()
		r.Close()
	}, nil
}


	r, closer, err := buildGZipReader("./test.txt.gz")
	if err != nil {
		log.Fatal(err)
	}
	defer closer()
	counts, err := countLetters(r)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(counts

JSON


import (
	"encoding/json"
	"fmt"
)


f := struct {
Name string `json:"name"`
Age int `json:"age"`
}
err := json.Unmarshal([]byte(`{"name": "Bob", "age": 30}`), &f)

json pretty print

fmt.Println(json.MarshalIndent(data, "", "    "))

Example

import (
	"encoding/json"
	"fmt"
	"log"
)

type Person struct {
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	HairColor string `json:"hair_color"`
	HasDog    bool   `json:"has_dog"`
}

	myJson := `
[
	{
		"first_name": "Clark",
		"last_name": "Kent",
		"hair_color": "black",
		"has_dog": true
	},
	{
		"first_name": "Bruce",
		"last_name": "Wayne",
		"hair_color": "black",
		"has_dog": false
	}
]`

	var unmarshalled []Person

	//
	err := json.Unmarshal([]byte(myJson), &unmarshalled)
	if err != nil {
		log.Println("Error unmarshalling json", err)
	}
	log.Println(unmarshalled)

	// write json from a struct
	var mySlice []Person

	var m1 Person
	m1.FirstName = "Wally"
	m1.LastName = "West"
	m1.HairColor = "red"
	m1.HasDog = false

	mySlice = append(mySlice, m1)

	var m2 Person
	m2.FirstName = "Billy"
	m2.LastName = "Bob"
	m2.HairColor = "green"
	m2.HasDog = false

	mySlice = append(mySlice, m2)

	newJson, err := json.Marshal(mySlice)
	if err != nil {
		log.Println("error marshalling", err)
	}

	fmt.Println(string(newJson))

// ## Importing external packages not working with yaegi, however it works with gomacro. 
// This won't work with gobook until kernel changed to use gomacro
import (
	"context"
	"database/sql"
	"log"
	"time"

	_ "github.com/go-sql-driver/mysql"
)


func DoSomeInserts(ctx context.Context, db *sql.DB, value1 string, value2 string, value3 string) (err error) {
	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer func() {
		// If the transaction fails roll back
		if err != nil {
			tx.Rollback()
		}
		err = tx.Commit()
	}()
	_, err = tx.ExecContext(ctx, "INSERT INTO area (building_id, name, description, created_at, updated_at) values (?, ?, ?, NULL, NULL)", value1, value2, value3)
	if err != nil {
		return err
	}
	// use tx to do more database inserts here
	return nil
}


	db, err := sql.Open("mysql", "root:password@/test")
	if err != nil {
		panic(err)
	}
	// See "Important settings" section.
	db.SetConnMaxLifetime(time.Minute * 3)
	db.SetMaxOpenConns(10)
	db.SetMaxIdleConns(10)
	// Use context.Background for the parent context
	err = DoSomeInserts(context.Background(), db, "2", "test", "test_description")
	if err != nil {
		log.Fatal(err)
	}


import (
	"time"
	"fmt"
)

Use multiply to convert units to a time.Duration


d := 2 * time.Hour + 30 * time.Minute
fmt.Println(d)
2h30m0s

Using Equal method corrects for timezones


x := time.Now()
y := x
if(x.Equal(y)){
	fmt.Println("Equal!")
} else { 
	fmt.Println("Not equal!")
}
Equal!

Get current time with timezone


fmt.Println(time.Now())
2021-07-09 20:50:46.11267622 +0800 AWST m=+69.426266556

Very cool way of specifying date format, 1 represents seconds, 6 represents year, and 7 is timezone. Everything else is intuitive


tEarly := time.Parse("06-05-04 03:02:01", "20-01-01 01:01:01")
tLate := time.Parse("06-05-04 03:02:01", "20-01-01 12:01:01")
fmt.Println(tEarly)
2020-01-01 01:01:01 +0000 UTC

Print a portion of time


fmt.Println(tEarly.Year())
2020

Compare two times (before, after, equal)


fmt.Println(tEarly.Before(tLate))
true

Return the time elapsed between two dates


fmt.Println()

Algorithms


type IntTree struct {
	val         int
	left, right *IntTree
}


func (it *IntTree) Insert(val int) *IntTree {
	if it == nil {
		return &IntTree{val: val}
	}
	if val < it.val {
		it.left = it.left.Insert(val)
	} else if val > it.val {
		it.right = it.right.Insert(val)
	}
	return it
}


func (it *IntTree) Contains(val int) bool {
	switch {
	case it == nil:
		fmt.Println("No Match on", val)
		return false
	case val < it.val:
		fmt.Println("Going left", val, "<", it.val)
		return it.left.Contains(val)
	case val > it.val:
		fmt.Println("Going right", val, ">", it.val)
		return it.right.Contains(val)
	default:
		fmt.Println("Matched", val)
		return true
	}
}


func main() {
	t := IntTree{}
	t.Insert(10)
	t.Insert(10)
	t.Insert(9)
	t.Insert(5)
	fmt.Println(t.Contains(6))
	fmt.Println(t.Contains(5))
}


import (
	"errors"
	"fmt"
	"log"
)

Example of using an interface and type assertion to run a different method depending on the type


type treeNode struct {
	val    Val
	lchild *treeNode
	rchild *treeNode
}
type Val interface {
	process(int, int) int
}

type number int
type operator byte

func (n number) process(int, int) int {
	fmt.Println("Hey! Can't process this!")
	return int(n)
}

func (o operator) process(left int, right int) int {
	switch o {
	case '+':
		fmt.Printf("%v%v%v = %v\n", left, string(o), right, left+right)
		return left + right
	case '-':
		fmt.Printf("%v%v%v = %v\n", left, string(o), right, left-right)
		return left - right
	case '*':
		fmt.Printf("%v%v%v = %v\n", left, o, right, left*right)
		return left * right
	case '/':
		fmt.Printf("%v%v%v = %v\n", left, o, right, left/right)
		return left / right
	default:
		return -1
	}
}

func walkTree(t *treeNode) (int, error) {
	switch val := t.val.(type) {
	case nil:
		return 0, errors.New("invalid expression")
	case number:
		// we know that t.val is of type number, so return the
		// int value
		return int(val), nil
	case operator:
		// we know that t.val is of type operator, so
		// find the values of the left and right children, then
		// call the process() method on operator to return the
		// result of processing their values.
		left, err := walkTree(t.lchild)
		if err != nil {
			return 0, err
		}
		right, err := walkTree(t.rchild)
		if err != nil {
			return 0, err
		}
		return val.process(left, right), nil
	default:
		// if a new treeVal type is defined, but walkTree wasn't updated
		// to process it, this detects it
		return 0, errors.New("unknown node type")
	}
}

func main() {
	t := treeNode{
		val: number(10),
	}
	t2 := treeNode{
		val: number(9),
	}
	t3 := treeNode{
		val:    operator('-'),
		lchild: &t,
		rchild: &t2,
	}

	x, err := walkTree(&t3)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(x)
}

Slow implementation


import "fmt"


type LinkedList struct {
	Value interface{}
	Next  *LinkedList
}


func (ll *LinkedList) Insert(pos int, val interface{}) *LinkedList {
	if ll == nil || pos == 0 {
		return &LinkedList{
			Value: val,
			Next:  ll,
		}
	}
	ll.Next = ll.Next.Insert(pos-1, val)
	return ll
}


l := LinkedList{
	Value: 200,
}
m := LinkedList{
	Value: 300,
	Next:  &l,
}

fmt.Println(m.Value, m.Next.Value)
300 200
{300 0xc0002ec0e0} 0xc0002ec0e0


package main


import "fmt"


func reverse(x int) int {
	var result int
	for x != 0 {
		// multiply by 10 for placeholder for next digit
		result *= 10
		// Gets the last digit
		xModulo := x % 10
		// Add the last digit
		result += xModulo
		if result > 2147483647 || result < -2147483648 {
			return 0
		}
		// strip the last digit, no remainder because int
		x /= 10
	}
	return result
}


func main(){
	x := reverse(-4232)
	fmt.Println(x)
	fmt.Prin
}

MongoDb

go get go.mongodb.org/mongo-driver/mongo

Golang-jwt

  • jwt.ParseWithClaims(accessToken)

  • ParseUnverified(tokenString, claims) split token on ., make sure contains three parts

token = &Token{Raw: tokenString}

headerBytes

eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9

eyJDbGllbnRJRCI6ImM5ZjBhMmM1LTIxY2QtNDhiYS05MGJkLTA3NmUwYjIxZDQzNSIsIlRva2VuVVVJRCI6ImYzOWM2NjQwLTA1ZjEtNGU3Ny04ZTcyLWQ2ZDdmZjAyMzVjMiIsIktleUlEIjoiM2FiMTFiZDgtY2NhMC00YjMyLTlmNDctNTg2MWIwN2ZjZTc3IiwiZXhwIjoxNjM2NjI1NDM3LCJpYXQiOjE2MzY2MjQ4Mzd9

NYmcVkPDNSWSzWlATPLdxTN14dIhQiYk87jCjwC_4YsB5IiaAtezPPRVFr--7QKFHNpObjMjBRjCJcqpi9uCrQ