A Quick Intro to Golang

Posted on Apr 1, 2020

Go is one of the most beautiful and efficient language i worked with so far. If you are an experienced programmer, you can learn to code in Go within few weeks, because the language is fairly small and concise. The purpose of this guide is to introduce the core tools and mechanics in Go and references are provided to dig more.

I wrote some code snippets in Go for additional reference: https://github.com/arvryna/go-guide/

Array

  • syntax: words := [2]string{"apple", "orange"}
  • static size (defined ahead)
  • array are value types
  • deep copy is possible
  • NOTE: contrary to other languages, types differ with size in go, array of len 1 != array of len 2, if you pass array of wrong size to method params, compiler throws error
  • arrays are passed by value

Slice

  • Default value of slice is nil
  • Stored in heap, no need to set size in advance, grows as you append new objects
  • Append algorithm here: https://go.dev/src/runtime/slice.go#L192
  • It is possible to set size in advance to get predictable performance and to avoid troubling GC with alloc/dealloc etc
  • Quantified by length(occupied slots) and capacity(total slots)
  • Can use make to initialize words := make([]string, "one", "two")
  • Iteration(are ordered) using range keyword, for index, val := range slice { fmt.Println(val) }

Strings

  • Strings are immutable
  • Empty value of string is ""
  • Raw strings can be defined using back-ticks, and it provides multi-line string s
  • String concat s3 := s1 + s2
  • Methods: Contains, Compare, Count occurancess, Prefix check, Get index, Join, Split etc
  • Package for formatting: fmt; Printf, Sprintf
  • Convert from string to number num := strconv.Atoi("100")

Maps

  • Default value of map is nil
  • To be keys of map, it needs to be comparable eg., array, struct and primitives
  • These can’t be keys of map: Slice, Func and struct that contains slice or map
  • maps are not thread safe, Sync.Map is thread safe
  • syntax: var m map[string]int or m1 := make(map[string]int)
  • map length can be optionally pre-allocated
  • collections are Unordered
  • default value of map is nil
  • iteration(unordered) using range keyword, for key, val := range slice { fmt.Println(val) }

Struct:

  • Smallest building block in Go
  • Size of empty struct is 0 bytes
  • Go doesn’t have OOP, we can use struct to realize some of the OOP

Interface:

  • Default value of interface is nil
  • Interface contains set of method signatures

Functions:

  • funcs are first class citizens in go
  • Go also provides clousure with the help of anonymous functions
  • anonymous functions can be launched as go routines

Channels:

  • For communication between two go-routines
  • Types: buffered, unbuffered
  • Channel writing and reading are blocking operations upon hitting its limits
  • Syntax m := make(chan string); m <- "message"
  • Do not communicate by sharing memory; instead, share memory by communicating.

Go routines:

  • Lightweight threads, any function can be launched as a go-routine, if it is called with prefix go
  • Default tack size of go (as of 1.18) is 2kb and “grows” as needed
  • Communication between go-routines can be done via, channels or variables(but need to be protected from race conditions using locks)
  • go-routines are made to be highly concurrent, context switching among go-routines by go scheduler(runtime) is not as expensive as task switching kernel threads, hence lightweight, so in principle it is possible to launch a huge amount of goroutines up until the point where memory “runs out”

Sync package:

  • Provides ability to control the concurrency and to make things less crazy :)
  • tools: Waitgroup, Locks, Concurrent Map, Pool, Atomic

Others:

  • Slices, maps and channels can be created with the built-in make function, memory is initialized with zero values.

Nuances:

  • Different ways of initializing slices
var nilSlice []int 
emptySlice1 := make([]int, 0)
emptySlice2 := []int{}

fmt.Println(nilSlice == nil)    // true
fmt.Println(emptySlice1 == nil) // false
fmt.Println(emptySlice2 == nil) // false
  • defer: Each deferred call is put on stack, and executed in reverse order when the surrounding function ends. The reversed order helps deallocate resources correctly.
  • Gc pauses are usually under 10ms

Ref: