Go | Golang
Resources
Udemy - Learn Go for Beginners Crash Course
Installation
https://go.dev/doc/install
Installation - VS Code
1. Extensions > Go
2. cmd + shift + p to open command palette in VS Code
3. install / update tools > select all checkboxes and install
Getting Started
Starting a project
Create a main.go file.
Files require a package header declaration.
package main
import "fmt"
func main() {
fmt.Println("Hello world")
}
Running a project
go run .
Building a Project
go build -o {{name}} main.go
Packaging
go mod init {{name}}
go mod tidy
Tooling
gofmt / go fmt (formatting)
go vet (linting)
delve (debugging)
Variables
Standard Declaration
var myVar string = "something"
altVar := "something"
Constant Declaration
const constVar = "something"
Naming Convention
var privateArg = `private` // camel-case are private
var PublicArg = `public` // pascal-case are public
Types
Basic Types
rune(char), string, int, float, bool
Aggregate Types
1. Array strings - []string
var animals []string
animals = append(animals, "dog")
animals = append(animals, "cat")
animals = append(animals, "bird")
animals[0] // "dog"
indexToRemove := 1
slices.Delete(animals, indexToRemove, indexToRemove + 1)
fmt.Println(animals) // ["dog", "bird"]
2. Array of Array of Strings - [][]string
3. Struct
type User struct {
UserName string
Age int
FavouriteNumber float64
}
Reference Types
1. Hashmap - Map[string]string
// hashmaps do not maintain order
intMap := make(map[string]int)
intMap["one"] = 1
for key, val := range intMap {
fmt.Println(key, val)
}
delete(intMap, "one")
val, ok := intMap["four"]
if ok { fmt.Println(val, "is in map") }
2. Pointer - &x | *pointer
x := 10
pointer := &x
*pointer = 15
fmt.Println(x) // 15
3. Channels
Mostly used by goroutines (concurrency)
Blocking
func listener() {
key := chn
fmt.Println(key) // 'a'
}
func main () {
chn := make(chan rune)
go listener()
chn <- 'a' // trigger
}
4. Interface Types
type Animal interface {
says() string
howManyLegs() int
}
type Dog struct {
Sound string
NumberOfLegs()
}
func (d *Dog) says() {
return d.Sound
}
Decision
If-Else
x := true
if x {
fmt.Println("hi x")
} else {
fmt.Println("where did x go?")
}
Switch
Fancy if-else
val := "some-case"
switch val {
case "a":
// do something
case "b":
// do something
default:
fmt.Println("case not found")
}
Select
Used mostly together with channels
Allows for concurrent channel operations
package main
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "ch1 done"
}()
go func() {
time.Sleep(3 * time.Second)
ch2 <- "ch2 done"
}()
select {
case msg1 := <- ch1:
fmt.Println(msg1)
case msg2 := <- ch2:
fmt.Println(msg2)
case <- time.After(5 * time.Second):
fmt.Println("timeout")
}
}
Coding Flow
Looping
Standard 3 part loop
func main() {
for i := 0; i <= 10; i++ {
fmt.Println(i)
}
}
Conditinal loop
func main() {
i := 1000
for i > 100 {
fmt.Println(i)
i++
}
}
Infinite loop
for { ... }
Debugging
Delve
requires a go.mod file - do go mod init {{name}}
Create a launch.json file
Uses VS Code red dot breakpoint
Run > Start Debugging
//launch.json
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "main.go"
}
]
}
Comma-Ok
Go's way of error handling.
Returns a tuple - (value, err / ok)
Check against err / ok to know if the function ran correctly
// operation
value, ok := someFunction()
// channel
value, ok := <- chn
// maps
value, ok := myMap[key]
String Interpolation
%s - string
%d - int
%.2f - float 2 decimal place
%t - bool
fmt.Printf("%s %d %t", "hi", 1, True)
Regex
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
input = reg.RepalceAllString(input, "")
Operators
Math
add := 5 + 2
subtract := 5 - 2
divide := 1 / 2
multiply := 2 * 3
bracket := (2 + 3) * 5
power := math.Pow(2, 3)
remainder := 5 % 2
multiplyShort *= 2
divideShort /= 2
Unary
x := 3
x++
x--
Logical
greater x > y
lesser x < y
logicalAnd &&
logicalOr ||
Race Conditions
goRoutines can result in race conditions.
Resolve by using mutex
Test and check using --race flag
var msg string
var wg sync..WaitGroup
func updateMsg(s string) {
defer wg.Done()
msg = s
}
func main() {
msg = "Hello world"
wg.Add(2)
go updateMessage("hello universe")
go updateMessage("hello there")
wg.Wait()
fmt.Println(msg)
}
Codes with race conditions will show up with a warning
go run --race .
var msg string
var wg sync..WaitGroup
func updateMsg(s string, m *sync.Mutex) {
defer wg.Done()
defer m.Unlock()
m.Lock()
msg = s
}
func main() {
msg = "Hello world"
var mutex = sync.Mutex
wg.Add(2)
go updateMessage("hello universe", &mutex)
go updateMessage("hello there", &mutex)
wg.Wait()
fmt.Println(msg)
}
Channel VS Mutex
Channel -
Passes data, distributes units of work, communicate async results
Mutex -
cache, state
Concurrency Patterns
Worker Pools
Use a limited pool of workers to work against a queue
func worker(
id int,
jobs <- chan int,
results chan <- int,
wg *sync.WaitGroup
){
defer wg.Done()
for i := range jobs {
results <- j*2
}
}
func main() {
jobs := make(chan int, 5)
results := make(chan int, 5)
var wg sync.WaitGroup
// start 3 workers
for w := 0; w <3; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// send jobs
for j := 1; j <= 20; j++ {
jobs <- j
}
close(jobs)
wg.Wait()
close(results)
for r := range results {
fmt.Println("results ", r)
}
}
Fan-in Fan-out
Expands a pool of workers, then merges the results
func generator(nums ...int) <- chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
time.Sleep(100 * time.Millisecond)
}
close(out)
}()
return out
}
type result struct {
workerId int
input int
output int
}
func worker(id int, in <- chan int) <- chan result {
res := make(chan result)
go func() {
for n:= range in {
res <- result{
workerId: id,
input: n,
output: n*n
}
}
close(res)
}()
return res
}
func merge(workers ...<-chan result) <- chan result {
var wg sync.WaitGroup
out := make(chan result)
output := func(worker <- chan result) {
for res := range worker {
out <- res
}
wg.Done()
}
wg.Add(len(workers))
for _, worker := range workers {
go output(worker)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
func main() {
in := generator(1,2,3,...,15)
var workers [] <- chan result
for i := 0; i <5; i++ {
workers = append(workers, worker(i, in))
}
for res := range merge(workers...) {
fmt.Println(res)
}
}
Pipeline
Chains calls in sequence
func add(nums ...int) <- chan int {
out := make(chan int)
go func(){
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <- chan int) <- chan int {
out := make(chan int)
go func(){
for n := range in {
out <- n*n
}
close(out)
}()
return out
}
func main() {
for n := range sq(sq(add(1,2,3,4,5))) {
fmt.Println(n)
}
}
Testing
// xxx_test.go
func TestAdd(t *testing.T) {
got := addTwoNumbers(2,3)
want := 5
if got != want {
t.Error("got %d, wanted %d", got, want)
}
}
// run current dir, verbose
go test . -v
// run all tests
go test ./...
// run only specific test
go test ./xxx -run TestFunctionName -v
HTTP Server
Raw HTTP server
func homePage(w. http.ResponseWrite, r *http.Request) {
html := <strong>htllo world</strong>
w.Header().Set("Content-Type", "text/html)
w.Write(html)
}
func main() {
http.HandleFunc("/", homePage)
http.ListenAndServe(":8000", nil)
}