the-go-bits

I recently finished "The Go Programming Language". It is very well written, in-depth book. The learning curve is very smooth, I think even relatively new programmers may use this book as intro to their first programming languages. Otherwise, check out O'Reilly "Introducing Go: Build Reliable, Scalable Programs", which I think is much friendlier but covers less.

Here is a raw list of some interesting bits that I learned primarily from "The Go Programming Language" I collected for my future reference.

* there are constants, variables (addressable values) and non-addressable values (e.g. anonymous structs, pointer to return of a function). Variable is something that stores value and has a name (e.g. x) or can deduce name from the left hand side (x.foo or x[i])
* lexical scope — piece of code where symbols can collide and resolved (function body, if, for, switch statements, package)
* can override variable names in nested lexical scopes
* package is the most outer lexical scope
* lifetime of a variable != lexical scope

* there is no distinction between heap and stack
* operator address-of "&" can be applied only to variables
* pointer is an address of a variable. All variables have addresses and can have pointer, but not all values have pointers
* pointer is not a numerical address of OS level memory
* pointer can be used to compare to null, compare to other pointer, dereference, or get address of variable. There is no pointer arithmetic
* it is perfectly fine to return address of a local variable in function
* function "new" creates new unnamed variable and returns its pointer (it is same as returning reference to local variable in function)

* swapping variables: "i, j = j, i" also can do "i, j, k = j, k, i" etc.
* flag package has nice API for arguments used to run program

* if package has "init" function it will be run once for go program when it is first time imported
* numeric types compare only within type (1.(int64) != 1.(int32))

* all Go source code is in UTF-8
* strings inside Go code are string-literals, and they are in UTF-8 too
* type string = byte[]
* UTF-8 is multi-byte encoding that is fully compatible with ASCII. It can represent all languages and more. It is efficient and can represent a lot of numbers. Sorting UTF-8 by bytes is lexicographical sorting. It has a number other benefits too. Few of co-authors of Go are co-authors of UTF-8. There is also UTF-16 and UTF-32, they are simpler but less memory efficient.
* use runes to work with UTF-8 strings
* use bytes[] for non UTF-8 strings, handle encoding yourself of by 3rd party module
* modules strings and unicode are dealing with UTF-8
* watch out for filepath, since filepath has to be in encoding of OS, which can vary. For example it can use Korean or Japanese non UTF-8 encoding

* constants are untyped, which allows to use "1" and determine at compile time if it is int64, uint64, float, etc. 
* constant expression iota allows to iterate in definition of consts. Useful for enums and bitsets
* numerical constats can be very large. It is safe to assume that it is 512 bits long. 
* constants do checks at compile time for division on zero etc. So that you always get a usual number in at compile time.

* [...]int64{1,2,3} notation is nice for arrays that you don't want to count how many elements it has
* slice points to element in array, to size of elements and capacity. Very safe and space efficient structure.
* to append to slice use "x = append(x, y...)" where x,y are slices. "y..." is expansion of slice into list of arguments.
* use "func x(args... int)" for variable number of arguments
* often useful not to "pop" from slice, but just to reduce slice, and consider values beyond slice as garbage. Very convenience since no need to explicitly free memory.
* maps are not guaranteed to have order, sometimes go runtime changes order from run-to-run of same code
* in maps key should be comparable with "==", which is a lot of types, even functions!

* functions are first class in go, meaning they can be passed as arguments or assigned to variables
* closures are possible. closure is a function with access to the scope they have been defined in
* function variables can be compared by their pointers

* can use shorthand to defined multiple fields with same type in structs "type A struct {a,b int}"
* structs literals can have named fields form like "x := Point{X: 1, Y: 2, name: "asdf"}" for publicly exposed fields
* Promotion mechanism. Stucts can be embedded with like "type Window{Box, size int}" where Box is a type. This will result in creating public field Box and syntactic sugar to refer to methods of Box field, so no need to chain methods of fields.

* type error = string
* error interface is single function "Error() string"
* if you have logic to check if error is specific type of error by using type assertion (like EOD error), you need to do that immediately up the call stack. This is because "fmt.Errorf" is used often and it takes error and makes a string from it, which discards previous concrete type and you would not be able to do type assertions anymore. TODO: what if you still want to? 

* methods have resolution mechanism. If receiver is pointer, it can be used with variable. If receiver is variable, it can be used with pointer. And of course it can be used with same receiver type it was defined with

* bitset implemented with numbers is nice, since you can have fast union and other bitwise logic.
* interface containing a nil pointer is not nil. So if you have pointer to interface, then it will never be nil. This is because interface internally contains interface value and interface type, and interface type is not nil always.

* there is a sort package that can be used to sort with arbitrary compare function. To use it need to define a function that returns new type that implements sortable interface (Len(),  Less(i,j), Swap(i,j)).
* there is type-switch notation "switch x.(type)" or "switch x := x.(type)" if you want to use x with that time in switch scope

* there is main goroutine that starts with main program
* goroutine has variable stack associated, OS threads have 4MB, goroutine can have as low as 4KB and as much as 1GB, which allows for very large number of goroutines and very deep recursive calls

* channels are useful when they are buffered and no single goroutine drain whole channel all the time
* mnemonics for channels "<-chan" read channel, and read from channel. "chan<-" write channel, write to channel
* channel made without size is unbuffered, it will block immediately whoever reads or writes to it
* leaking goroutines is goroutines are trying to write to channel that nobody is reading from. This is not checked by runtime. Need to close such channels with WaitGroup for example so that those goroutines will panic on write to or stop reading from. 
* binary semaphore is useful for running single goroutine "make(chan bool, 1)"
* counting semaphore is useful for limiting number of goroutines (so we don't exhaust OS resources): "make(chan bool, 20)"
* use sync.WaitGroup to synchronize that all channels are finished
* use sync.Once for one time initialization in concurrent environment, also called Lazy Initialization
* use sync.Mutes Lock and Unlock
* use sync.RWMutes if you have exlusive lock (for read and write) and non-exclusive read only ownership.
* Memory Synchronization. It is important to use locks correctly to help runtime and OS optimize CPU level caching for concurrent execution. Mutexes can signal to OS to flush the cache.
* use race detector tool

* go has vendor specific config for packages, so you can remap where to get packages from
* can run godoc server locally
* if package contains "internal" name it can not be imported from more than current level of packages (different parent)
* circular dependencies are not allowed
* in tests to avoid circular dependencies, make new module that is imported in both modules. For tests "backdoor" private module level variables into export single test file, this file often called "export_test.go"

* go has tests (Test...), benchmarks (Benchmark...) and examples (Example) for testing
* elaborate testing frameworks are discouraged "they feel like foreign language"
* define tests very simple first, it is bad to have abstract test utilities
* white-box testing — test that internals of package are correct, not just interface.
* avoid brittle tests —tests that change often with changes in code, try to make tests such that they remain even if code changes (e.g. match substring of error message)
* go command line has tool for coverage reporting and visualization
* go benchmark is easy to use and define
* for benchmarking use different sizes of input and algorithms to see their behavior and comparative advantage
* go command line also has profiling. Profiling shows which functions use most CPU, memory, or blocking 
* examples in testing are used in godoc to show how code is used. This is nice since it is showcases how to run it and go compiler checks that code is correct. If it has "Output" comment in body, then go test will check that it produces same output to std out.

* reflection, powerful, but easy to make mistakes. better stay away from it
* unsafe allows access OS memory address. However, addresses can change during runtime, Go spec does not guarantee their stability.
* layout is not guaranteed by Go spec
* unsafe.Sizeof may be useful to check size of structs
* calling C code looks easy in Go. Calling go code in C also possible. Check cgo module.