# Golang Recipes [Golang Recipes](https://github.com/ncthuc/golang-recipes) ###### tags: `Go` [TOC] # Basic syntax ## Data types ```go bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // alias for uint8 rune // alias for int32 represents a Unicode code point float32 float64 complex64 complex128 ``` The `int`, `uint`, and `uintptr` types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems. ## Formatted I/O - package `fmt` ### Print using default formaters - The bery basic ```go import "fmt" func main() { fmt.Println("Hello, Golang") } ``` - `Sprint` & `Sprintln` - print list of operands to a string using default formats. With `Sprint`, spaces are added between operands when neither is a string. In case of `Sprintln`, spaces are always added between operands and a newline is appended. ```go const name, age = "Kim", 22 s := fmt.Sprint(name, " is ", age, " years old.\n") s2 := fmt.Sprintln(name, "is", age, "years old.") fmt.Print(s) fmt.Print(s2) ``` Output: ```shell Kim is 22 years old. Kim is 22 years old. ``` ### Print using format specfiers - Format specifier verbs - `%v` - the value in a default format when printing structs, the plus flag (`%+v`) adds field names - `%T` - a Go-syntax representation of the type of the value - `%d`, `%4d` (right justified), `%-4d` (left justified), `%04d` - integer - `%s` - string - `%f`, `%.2f`, `9.2%f` - float - `%t` - bool - `%q` - a double-quoted string safely escaped with Go syntax - `Sprintf` - print to a string using a format specifier ```go const name, age = "Kim", 22 s := fmt.Sprintf("%s is %d years old.\n", name, age) ``` - `Printf` - print with format specifier ## Variables ### Declare Using `var` statement, can be at package or function level. ```go var c, python, java bool func main() { var i int fmt.Println(i, c, python, java) } ``` ### Decalare with initializers The type can be omitted. ```go var i, j int = 1, 2 func main() { var c, python, java = true, false, "no!" fmt.Println(i, j, c, python, java) } ``` ### Short variable declarations inside function with `:=` ```go func main() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java) } ``` ## Conditional statements ### The basic `if-else` statement The condition expression need not be surrounded by parentheses ( ) but the braces { } are required. ```go func sqrt(x float64) string { if x < 0 { return sqrt(-x) + "i" } else { return fmt.Sprint(math.Sqrt(x)) } } ``` ### `if` statement with short initialization statement Variables declared by the statement are only in scope until the end of the if. Go's switch cases need NOT be constants, and the values involved need NOT be integers. In Go, `break` statement is provided AUTOMATICALLY at the end of each case. ```go func pow(x, n, lim float64) float64 { if v := math.Pow(x, n); v < lim { return v } return lim } ``` ### switch statement - the basic Switch cases evaluate cases from top to bottom, stopping when a case succeeds. ```go fmt.Print("Go runs on ") switch os := runtime.GOOS; os { case "darwin": fmt.Println("OS X.") case "linux": fmt.Println("Linux.") default: // freebsd, openbsd, // plan9, windows... fmt.Printf("%s.\n", os) } ``` ### switch with no condition ```go t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } ``` ## Go loop ### The basic Unlike other languages like C, Java, or JavaScript there are no parentheses surrounding the three components of the for statement and the braces { } are always required. ```go sum := 0 for i := 0; i < 10; i++ { sum += i } fmt.Println(sum) ``` The init and post statements are optional. ```go func main() { sum := 1 for ; sum < 1000; { sum += sum } fmt.Println(sum) } ``` ### `for` used as `while` ```go sum := 1 for sum < 1000 { sum += sum } fmt.Println(sum) ``` Forever ```go for { } ``` ### `for-each` range loop Looping over elements in slices, arrays, maps, channels or strings is often better done with a range loop. ```go strs := []string{"hello", "world"} for i, s := range strs { fmt.Println(i, s) } ``` Ignore indices: ```go strs := []string{"hello", "world"} for _, s := range strs { fmt.Println(s) } ``` Ignore values: ```go strs := []string{"hello", "world"} for i := range strs { fmt.Println(i) } ``` # Go functions ## The basic ```go func add(x, y int) int { return x + y } func main() { fmt.Println(add(42, 13)) } ``` ## Return multiple results ```go func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("hello", "world") fmt.Println(a, b) } ``` ## Named return values ```go func split(sum int) (x, y int) { x = sum * 4 / 9 y = sum - x return } func main() { fmt.Println(split(17)) } ``` ## Variadic Functions ```go func sum(nums ...int) int { total := 0 for _, num := range nums { total += num } return total } func max(nums ...int) int { vmax := nums[0] for _, num := range nums { if num > vmax { vmax = num } } return vmax } func min(nums ...int) int { vmin := nums[0] for _, num := range nums { if num < vmin { vmin = num } } return vmin } func main() { fmt.Println(sum(2, 3, 5)) fmt.Println(max(2, 3, 5)) a := []int{2, 3, 5, 8} fmt.Println(sum(a...)) fmt.Println(max([]int{3, 5, 8, 2}...)) fmt.Println(min([]int{3, 5, 8, 2}...)) } ``` ## defer Defer is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup. ```go func main() { f := createFile("/tmp/defer.txt") defer closeFile(f) writeFile(f) } func createFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f } func writeFile(f *os.File) { fmt.Println("writing") fmt.Fprintln(f, "data") } func closeFile(f *os.File) { fmt.Println("closing") err := f.Close() if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) os.Exit(1) } } ``` ## Stacking defer Deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order. ```go func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } ``` Output: ```shell done 9 8 7 6 5 4 3 2 1 0 ``` ## Closures A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables. For example, the `adder` function returns a closure. Each closure is bound to its own `sum` variable. ```go package main import "fmt" func adder() func(int) int { sum := 0 return func(x int) int { sum += x return sum } } func main() { pos, neg := adder(), adder() for i := 0; i < 5; i++ { fmt.Println( pos(i), neg(-2*i), ) } } ``` Ouput: ``` 0 0 1 -2 3 -6 6 -12 10 -20 ``` Below is a `fibonacci` function that returns a function (a closure) that returns successive fibonacci numbers (0, 1, 1, 2, 3, 5, ...). ```go package main import "fmt" func fibonacci() func() int { f1 := 0 f2 := 1 return func() int { f2 = f1 + f2 f1 = f2 - f1 return f1 } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } } ``` ## Useful functions ### Min Max Sum ```go func sum(nums ...int) int { total := 0 for _, num := range nums { total += num } return total } func max(nums ...int) int { vmax := nums[0] for _, num := range nums { if num > vmax { vmax = num } } return vmax } func min(nums ...int) int { vmin := nums[0] for _, num := range nums { if num < vmin { vmin = num } } return vmin } // Usage: func main() { fmt.Println(sum(2, 3, 5)) fmt.Println(max(2, 3, 5)) a := []int{2, 3, 5, 8} fmt.Println(sum(a...)) fmt.Println(max([]int{3, 5, 8, 2}...)) fmt.Println(min([]int{3, 5, 8, 2}...)) } ``` ### GCD ```go func Gcd(a, b int) int { for b != 0 { a, b = b, a % b } return a } ``` ### Number of combinations ```go func Combination(n, k int) int { r := 1 for i := n-k+1; i<=n; i++ { r *= i } for i := 2; i<=k; i++ { r /= i } return r } ``` ### Prime check ```go func IsPrime(n int) bool { if n < 2 { return false } for i := 2; i * i <= n; i++ { if n % i == 0 {return false} } return true } ``` ### Fibonacci ```go func fibonacci(n int) int { f1 := 1 f2 := 1 for i := 1; i < n; i++ { f1, f2 = f2, f1 + f2 } return f1 } ``` ### Reverse string ```go func Reverse(s string) string { r := "" for _, c := range s { r = string(c) + r } return r } ``` ### Reverse array/slice ```go func ReverseInline(a []int) []int { l := 0 r := len(a)-1 for l < r { a[l], a[r] = a[r], a[l] l++ r-- } return a } func Reverse(a []int) []int { n := len(a) r := make([]int, n) for i, v := range a { r[n-1-i] = v } return r } ``` # Go strings ## Strings, bytes, runes and characters In Go, a string is in effect a read-only slice of bytes. ```go const World = "world" var hello = "hello" // Concatenate strings. var helloWorld = hello + " " + World helloWorld += "!" fmt.Println(helloWorld) // hello world! // slice fmt.Println(helloWorld[3:8]) // lo wo // Compare strings. fmt.Println(hello == "hello") // true fmt.Println(hello > helloWorld) // false ``` ## String funtions The standard library’s `strings` package provides many useful string-related functions. Samples: ```go import ( "fmt" s "strings" ) var p = fmt.Println func main() { p("Contains: ", s.Contains("test", "es")) p("Count: ", s.Count("test", "t")) p("HasPrefix: ", s.HasPrefix("test", "te")) p("HasSuffix: ", s.HasSuffix("test", "st")) p("Index: ", s.Index("teest", "e")) p("Last index:", s.LastIndex("teest", "e")) p("Count: ", s.Count("teest", "e")) p("Contains: ", s.Contains("teest", "ee")) p("Join: ", s.Join([]string{"a", "b"}, "-")) p("Split: ", s.Split("a-b-c-d-e", "-")) p("Repeat: ", s.Repeat("a", 5)) p("Replace: ", s.Replace("foo", "o", "0", 1)) p("Replace: ", s.Replace("foo", "o", "0", -1)) p("ToLower: ", s.ToLower("TEST")) p("ToUpper: ", s.ToUpper("test")) p("TrimSpace: ", s.TrimSpace(" \n\n\n\n test ")) p("Len: ", len("hello")) p("Char:", "hello"[1]) } ``` Output: ```shell Contains: true Count: 2 HasPrefix: true HasSuffix: true Index: 1 Last index: 2 Count: 2 Contains: true Join: a-b Split: [a b c d e] Repeat: aaaaa Replace: f0o Replace: f00 ToLower: test ToUpper: TEST TrimSpace: test Len: 5 Char: 101 ``` # Data type conversion ## Between basic types ```go var m int = 123 var n int64 = int64(m) s := "string" fmt.Println(n) // 123 fmt.Println(len(string(s[0]))) // 1 ``` ## int/int64 to string Using `strconv.Itoa`: ```go import ( "fmt" "strconv" ) func main() { fmt.Println(strconv.Itoa(97)) // "97" } ``` Using `strconv.FormatInt`: ```go package main import ( "fmt" "reflect" "strconv" ) func main() { var i int64 = 1234 s := strconv.FormatInt(i, 10) fmt.Println(s) //1234 fmt.Println(strconv.FormatInt(i, 2)) //10011010010 fmt.Println(reflect.TypeOf(s)) //string } ``` ## string to int/int64 Using `strconv.Atoi`: ```go s := "97" n, _ := strconv.Atoi(s) fmt.Println(n+1) // 98 ``` With error handling: ```go s := "97" if n, err := strconv.Atoi(s); err == nil { fmt.Println(n+1) } else { fmt.Println(s, "is not an integer.") } // Output: 98 ``` **Warning**: `string(97)` return `"a"` Using `strconv.ParseInt`: ```go package main import ( "fmt" "reflect" "strconv" ) func main() { var s string = "1234" i, err := strconv.ParseInt(s,10,64) if err != nil { panic(err) } fmt.Println(i) //1234 fmt.Println(reflect.TypeOf(i)) //int64 } ``` # Regular Expressions [Regular Expressions](https://gobyexample.com/regular-expressions) TBD # Arrays and slices ## Arrays ```go var a [5]int fmt.Println("emp:", a) a[4] = 100 fmt.Println("set:", a) fmt.Println("get:", a[4]) fmt.Println("len:", len(a)) // declare and initialize b := [5]int{1, 2, 3, 4, 5} fmt.Println("dcl:", b) // Have the compiler count the array elements c := [...]string{"Penn", "Teller"} fmt.Printf("%v %T", c, c) // [Penn Teller] [2]string d := [...]string{ "Penn", "Teller", // trailing comma is required here } fmt.Printf("%v %T", d, d) // multi-dimensional var twoD [2][3]int for i := 0; i < 2; i++ { for j := 0; j < 3; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) ``` Output ```shell emp: [0 0 0 0 0] set: [0 0 0 0 100] get: 100 len: 5 dcl: [1 2 3 4 5] 2d: [[0 1 2] [1 2 3]] ``` ## Slices ```go // A slice literal is declared just like an array literal, except you leave out the element count: letters := []string{"a", "b", "c", "d"} fmt.Printf("%v %T", letters, letters) // [a b c d] []string s := make([]string, 3) fmt.Println("emp:", s) s[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) fmt.Println("get:", s[2]) fmt.Println("len:", len(s)) s = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s) c := make([]string, len(s)) copy(c, s) fmt.Println("cpy:", c) l := s[2:5] fmt.Println("sl1:", l) l = s[:5] fmt.Println("sl2:", l) l = s[2:] fmt.Println("sl3:", l) // declare and initialize t := []string{"g", "h", "i"} fmt.Println("dcl:", t) // multi-dimensional slice, the length of the inner slices can vary twoD := make([][]int, 3) for i := 0; i < 3; i++ { innerLen := i + 1 twoD[i] = make([]int, innerLen) for j := 0; j < innerLen; j++ { twoD[i][j] = i + j } } fmt.Println("2d: ", twoD) ``` Output: ```shell $ go run slices.go emp: [ ] set: [a b c] get: c len: 3 apd: [a b c d e f] cpy: [a b c d e f] sl1: [c d e] sl2: [a b c d e] sl3: [c d e f] dcl: [g h i] 2d: [[0] [1 2] [2 3 4]] ``` ## Slide tricks https://github.com/golang/go/wiki/SliceTricks ### Append a vector ```go a = append(a, b...) ``` ### Copy ```go b = make([]T, len(a)) copy(b, a) // or b = append([]T(nil), a...) // or b = append(a[:0:0], a...) // See https://github.com/go101/go101/wiki ``` ### Cut ```go a = append(a[:i], a[j:]...) ``` To avoid memory leak: ```go copy(a[i:], a[j:]) for k, n := len(a)-j+i, len(a); k < n; k++ { a[k] = nil // or the zero value of T } a = a[:len(a)-j+i] ``` ### Delete ```go a = append(a[:i], a[i+1:]...) // or a = a[:i+copy(a[i:], a[i+1:])] ``` To avoid memory leak: ```go copy(a[i:], a[i+1:]) a[len(a)-1] = nil // or the zero value of T a = a[:len(a)-1] ``` ### Expand ```go a = append(a[:i], append(make([]T, j), a[i:]...)...) ``` ### Extend ```go a = append(a, make([]T, j)...) ``` ### Filter (in place) ```go n := 0 for _, x := range a { if keep(x) { a[n] = x n++ } } a = a[:n] ``` ### Insert ```go a = append(a[:i], append([]T{x}, a[i:]...)...) ``` or: ```go s = append(s, 0 /* use the zero value of the element type */) copy(s[i+1:], s[i:]) s[i] = x ``` ### Insert a vector ```go a = append(a[:i], append(b, a[i:]...)...) ``` ### Push ```go a = append(a, x) ``` ### Pop ```go x, a = a[len(a)-1], a[:len(a)-1] ``` ### Push front / unshift ```go a = append([]T{x}, a...) ``` ### Pop front/Shift ```go x, a = a[0], a[1:] ``` ### Reversing ```go for left, right := 0, len(a)-1; left < right; left, right = left+1, right-1 { a[left], a[right] = a[right], a[left] } ``` Or: ```go for i := len(a)/2-1; i >= 0; i-- { opp := len(a)-1-i a[i], a[opp] = a[opp], a[i] } ``` ### Batching with minimal allocation ```go actions := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} batchSize := 3 batches := make([][]int, 0, (len(actions) + batchSize - 1) / batchSize) for batchSize < len(actions) { actions, batches = actions[batchSize:], append(batches, actions[0:batchSize:batchSize]) } batches = append(batches, actions) ``` Yields the following: ```shell [[0 1 2] [3 4 5] [6 7 8] [9]] ``` ## Sorting & Searching Using `sort` package. ### Sort a slice of ints, float64s or strings Using: - `sort.Ints` - `sort.Float64s` - `sort.Strings` ```go package main import ( "fmt" "sort" ) func main() { s := []int{4, 2, 3, 1} sort.Ints(s) fmt.Println(s) // [1 2 3 4] } ``` ### Check if a slice is sorted - `sort.IntsAreSorted(a []int) bool` - `sort.Float64sAreSorted(a []float64) bool` - `sort.StringsAreSorted(a []string) bool` ### Custom comparator - `sort.Slice(slice interface{}, less func(i, j int) bool)` - `sort.SliceStable(slice interface{}, less func(i, j int) bool)` - `sort.SliceIsSorted(slice interface{}, less func(i, j int) bool) bool` ```go package main import ( "fmt" "sort" ) func main() { people := []struct { Name string Age int }{ {"Alice", 25}, {"Elizabeth", 75}, {"Alice", 75}, {"Bob", 75}, {"Alice", 75}, {"Bob", 25}, {"Colin", 25}, {"Elizabeth", 25}, } // Sort by name, preserving original order sort.SliceStable(people, func(i, j int) bool { return people[i].Name < people[j].Name }) fmt.Println("By name:", people) // Sort by age preserving name order sort.SliceStable(people, func(i, j int) bool { return people[i].Age < people[j].Age }) fmt.Println("By age,name:", people) } ``` ### Generic type Type `sort.Interface`: ```go type Interface interface { // Len is the number of elements in the collection. Len() int // Less reports whether the element with // index i should sort before the element with index j. Less(i, j int) bool // Swap swaps the elements with indexes i and j. Swap(i, j int) } ``` - `sort.Sort(data Interface)`: Sort sorts data. It makes one call to data.Len to determine n, and O(n*log(n)) calls to data.Less and data.Swap. The sort is not guaranteed to be stable. - `sort.Stable(data Interface)`: Stable sorts data while keeping the original order of equal elements. - `sort.IsSorted(data Interface) bool`: reports whether data is sorted. ### Searching TBD https://golang.org/pkg/sort/#SearchInts # Maps ```go package main import "fmt" func main() { // create an empty map m := make(map[string]int) // set key m["k1"] = 7 m["k2"] = 13 fmt.Println("map:", m) // get key v1 := m["k1"] fmt.Println("v1: ", v1) // len: number of key-value pair fmt.Println("len:", len(m)) // delete a key delete(m, "k2") fmt.Println("map:", m) // check if a key is present _, ok := m["k2"] fmt.Println("present:", ok) // declare and initialize at the same time n := map[string]int{"foo": 1, "bar": 2} fmt.Println("map:", n) } ``` # Struct & pointer ## Pointer Go has pointers. A pointer holds the memory address of a value. The type `*T` is a pointer to a `T` value. Its zero value is `nil`. The `&` operator generates a pointer to its operand. The `*` operator denotes the pointer's underlying value. ```go func zeroval(ival int) { ival = 0 } func zeroptr(iptr *int) { *iptr = 0 } func main() { i := 1 fmt.Println("initial:", i) zeroval(i) fmt.Println("zeroval:", i) zeroptr(&i) fmt.Println("zeroptr:", i) fmt.Println("pointer:", &i) } ``` Output: ```shell $ go run pointers.go initial: 1 zeroval: 1 zeroptr: 0 pointer: 0x42131100 ``` ## Structs Go’s structs are typed collections of fields. You can safely return a pointer to local variable as a local variable will survive the scope of the function. You can name the fields when initializing a struct. Omitted fields will be zero-valued. Access struct fields with a dot. You can also use dots with struct pointers - the pointers are automatically dereferenced. ```go type person struct { name string age int } func newPerson(name string) *person { p := person{name: name} p.age = 42 return &p } func main() { fmt.Println(person{"Bob", 20}) fmt.Println(person{name: "Alice", age: 30}) fmt.Println(person{name: "Fred"}) fmt.Println(&person{name: "Ann", age: 40}) fmt.Println(newPerson("Jon")) s := person{name: "Sean", age: 50} fmt.Println(s.name) sp := &s fmt.Println(sp.age) sp.age = 51 fmt.Println(sp.age) } ``` ```shell $ go run test.go {Bob 20} {Alice 30} {Fred 0} &{Ann 40} &{Jon 42} Sean 50 51 ``` ## Methods Go supports *methods* defined on struct types. This `area` method has a *receiver* type of `*rect`. Methods can be defined for either pointer or value receiver types. Go automatically handles conversion between values and pointers for method calls. You may want to use a pointer receiver type to **avoid copying** on method calls or to allow the method to **mutate** the receiving struct. ```go package main import "fmt" type rect struct { width, height int } func (r *rect) area() int { return r.width * r.height } func (r rect) perim() int { return 2*r.width + 2*r.height } func main() { r := rect{width: 10, height: 5} fmt.Println("area: ", r.area()) fmt.Println("perim:", r.perim()) rp := &r fmt.Println("area: ", rp.area()) fmt.Println("perim:", rp.perim()) } ``` ```shell $ go run methods.go area: 50 perim: 30 area: 50 perim: 30 ``` ## Interfaces Interfaces are named collections of method signatures. To implement an interface in Go, we just need to **implement all the methods** in the interface. If a variable has an interface type, then we can call methods that are in the named interface. ```go package main import ( "fmt" "math" ) type geometry interface { area() float64 perim() float64 } type rect struct { width, height float64 } type circle struct { radius float64 } func (r rect) area() float64 { return r.width * r.height } func (r rect) perim() float64 { return 2*r.width + 2*r.height } func (c circle) area() float64 { return math.Pi * c.radius * c.radius } 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} measure(r) measure(c) } ``` ```shell $ go run interfaces.go {3 4} 12 14 {5} 78.53981633974483 31.41592653589793 ``` ### Type assertions TBD # Reuse code in Go (composition vs inheritance) TBD https://www.jawahar.tech/blog/golang-inheritance-vs-composition # Error handling ## Error as an explicit, separate return value In Go it’s idiomatic to communicate errors via an explicit, separate return value. ```go package main import "errors" func f1(arg int) (int, error) { if arg == 42 { return -1, errors.New("can't work with 42") } return arg + 3, nil // a nil value indicates "no error". } ``` ## Custom error types It’s possible to use custom types as errors by implementing the Error() method on them. ```go type argError struct { arg int prob string } func (e *argError) Error() string { return fmt.Sprintf("%d - %s", e.arg, e.prob) } func f2(arg int) (int, error) { if arg == 42 { return -1, &argError{arg, "can't work with it"} } return arg + 3, nil } ``` # Testing go code ## The basics The `testing` package provides the tools we need to write unit tests and the `go test` command runs tests. Testing code typically lives in the **same package** as the code it tests. For example, if the source file is `utils.go`, then the test file for it would then be named `utils_test.go`. Example function to be tested: ```go func IntMin(a, b int) int { if a < b { return a } return b } ``` And its test code: ```go func TestIntMinBasic(t *testing.T) { ans := IntMin(2, -2) if ans != -2 { t.Errorf("IntMin(2, -2) = %d; want -2", ans) } } ``` ## Test against multiple testcases using table-driven style ```go func TestIntMinTableDriven(t *testing.T) { var tests = []struct { a, b int want int // expected value }{ {0, 1, 0}, {1, 0, 0}, {2, -2, -2}, {0, -1, -1}, {-1, 0, -1}, } for _, tt := range tests { testname := fmt.Sprintf("%d,%d", tt.a, tt.b) t.Run(testname, func(t *testing.T) { ans := IntMin(tt.a, tt.b) if ans != tt.want { t.Errorf("got %d, want %d", ans, tt.want) } }) } } ``` # Godoc: documenting Go code Ref: https://blog.golang.org/godoc The convention is simple: to document a type, variable, constant, function, or even a package, write a **regular comment directly preceding its declaration**, with **no intervening blank line**. *Godoc* will then present that comment as text alongside the item it documents. For example, this is the documentation for the `fmt` package's `Fprint` function: ```go // Fprint formats using the default formats for its operands and writes to w. // Spaces are added between operands when neither is a string. // It returns the number of bytes written and any write error encountered. func Fprint(w io.Writer, a ...interface{}) (n int, err error) { ``` Notice this comment is a complete sentence that **begins with the name of the element it describes**. This important convention allows us to generate documentation in a variety of formats, from plain text to HTML to UNIX man pages, and makes it read better when tools truncate it for brevity, such as when they extract the first line or sentence. Comments on **package declarations** should provide general package documentation. These comments can be short, like the `sort` package's brief description: ```go // Package sort provides primitives for sorting slices and user-defined // collections. package sort ``` An example of more **complex godoc**: https://golang.org/src/encoding/gob/doc.go # Working with date and time ## Epoch ```go package main import ( "fmt" "time" ) func main() { now := time.Now() fmt.Printf("%v - %T\n", now, now) secs := now.Unix() nanos := now.UnixNano() millis := nanos / 1000000 fmt.Println(secs) fmt.Println(millis) fmt.Println(nanos) fmt.Println(time.Unix(secs, 0)) fmt.Println(time.Unix(0, nanos)) } /* 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 - time.Time 1257894000 1257894000000 1257894000000000000 2009-11-10 23:00:00 +0000 UTC 2009-11-10 23:00:00 +0000 UTC */ ``` ## Time ```go package main import ( "fmt" "time" ) func main() { p := fmt.Println now := time.Now() p(now) then := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) p("then:", then) p(then.Year(), then.Month(), then.Day(), then.Hour(), then.Minute(), then.Second(), then.Nanosecond(), then.Location()) p(then.Weekday()) p(then.Before(now), then.After(now), then.Equal(now)) } /* 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 then: 2009-11-17 20:34:58.651387237 +0000 UTC 2009 November 17 20 34 58 651387237 UTC Tuesday false true false */ ``` ## Time duration ```go package main import ( "fmt" "time" ) func main() { p := fmt.Println now := time.Now() p(now) then := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) p("then:", then) diff := then.Sub(now) p("diff:", diff) p(diff.Hours(), diff.Minutes(), diff.Seconds(), diff.Nanoseconds()) p(then.Add(diff)) p(then.Add(-diff)) p(then.Add(24 * time.Hour)) p(then.Add(30 * time.Second)) p(then.AddDate(1, 2, 3)) // add 1 year, 2 months, 3 days } /* 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 then: 2009-11-17 20:34:58.651387237 +0000 UTC diff: 165h34m58.651387237s 165.58295871867693 9934.977523120617 596098.651387237 596098651387237 2009-11-24 18:09:57.302774474 +0000 UTC 2009-11-10 23:00:00 +0000 UTC 2009-11-18 20:34:58.651387237 +0000 UTC 2009-11-17 20:35:28.651387237 +0000 UTC 2011-01-20 20:34:58.651387237 +0000 UTC */ ``` ## Time format & parsing Go doesn’t use *yyyy-mm-dd* layout to format a time. Instead, you format a special layout parameter > **`Mon Jan 2 15:04:05 MST 2006`** This date is easier to remember when written as **01/02 03:04:05PM ‘06 -0700**. In more detail, Go uses following constants to format date, refer [here](https://golang.org/src/time/format.go) ```go const ( stdLongMonth = "January" stdMonth = "Jan" stdNumMonth = "1" stdZeroMonth = "01" stdLongWeekDay = "Monday" stdWeekDay = "Mon" stdDay = "2" stdUnderDay = "_2" stdZeroDay = "02" stdHour = "15" stdHour12 = "3" stdZeroHour12 = "03" stdMinute = "4" stdZeroMinute = "04" stdSecond = "5" stdZeroSecond = "05" stdLongYear = "2006" stdYear = "06" stdPM = "PM" stdpm = "pm" stdTZ = "MST" stdISO8601TZ = "Z0700" // prints Z for UTC stdISO8601ColonTZ = "Z07:00" // prints Z for UTC stdNumTZ = "-0700" // always numeric stdNumShortTZ = "-07" // always numeric stdNumColonTZ = "-07:00" // always numeric ) ``` Example: ```go package main import ( "fmt" "time" ) func main() { p := fmt.Println t := time.Now() p("t:", t.Format(time.RFC3339)) t1, e := time.Parse(time.RFC3339, "2012-11-01T22:08:41+00:00") p("t1:", t1) p(t.Format("3:04PM")) p(t.Format("Mon Jan _2 15:04:05 2006")) p(t.Format("2006-01-02T15:04:05.999999-07:00")) form := "3 04 PM" t2, e := time.Parse(form, "8 41 PM") p("t2:", t2) fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) ansic := "Mon Jan _2 15:04:05 2006" _, e = time.Parse(ansic, "8:41PM") p(e) } /* t: 2009-11-10T23:00:00Z t1: 2012-11-01 22:08:41 +0000 UTC 11:00PM Tue Nov 10 23:00:00 2009 2009-11-10T23:00:00+00:00 t2: 0000-01-01 20:41:00 +0000 UTC 2009-11-10T23:00:00-00:00 parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": cannot parse "8:41PM" as "Mon" */ ``` ### More format [constants](https://golang.org/pkg/time/#pkg-constants) ```go const ( ANSIC = "Mon Jan _2 15:04:05 2006" UnixDate = "Mon Jan _2 15:04:05 MST 2006" RubyDate = "Mon Jan 02 15:04:05 -0700 2006" RFC822 = "02 Jan 06 15:04 MST" RFC822Z = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone RFC850 = "Monday, 02-Jan-06 15:04:05 MST" RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST" RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700" // RFC1123 with numeric zone RFC3339 = "2006-01-02T15:04:05Z07:00" RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00" Kitchen = "3:04PM" // Handy time stamps. Stamp = "Jan _2 15:04:05" StampMilli = "Jan _2 15:04:05.000" StampMicro = "Jan _2 15:04:05.000000" StampNano = "Jan _2 15:04:05.000000000" ) ``` ## Working with timezone ```go package main import ( "fmt" "time" ) func main() { now := time.Now() fmt.Printf("%v - %T\n", now, now) fmt.Println(now.Local()) fmt.Println(now.Location()) fmt.Println(now.Zone()) loc, _ := time.LoadLocation("Asia/Ho_Chi_Minh") fmt.Println(loc) fmt.Println(now.In(loc)) } /* 2009-11-10 23:00:00 +0000 UTC m=+0.000000001 - time.Time 2009-11-10 23:00:00 +0000 UTC Local UTC 0 Asia/Ho_Chi_Minh 2009-11-11 06:00:00 +0700 +07 */ ``` # Goroutines ## The basic A goroutine is a lightweight thread managed by the Go runtime. ```go package main import ( "fmt" "time" ) func f(from string) { for i := 0; i < 3; i++ { fmt.Println(from, ":", i) time.Sleep(100 * time.Millisecond) } } func main() { // usual way, running synchronously f("direct") // invoke this function in a goroutine, f(s) will execute concurrently with the calling one go f("goroutine") // another function call. This will run asynchronously in separate goroutines with the above routine go func(msg string) { for i := 0; i < 3; i++ { fmt.Println(msg) time.Sleep(100 * time.Millisecond) } }("going") // Wait for them to finish (for a more robust approach, use a WaitGroup). time.Sleep(time.Second) fmt.Println("done") } ``` Output: ``` direct : 0 direct : 1 direct : 2 going goroutine : 0 goroutine : 1 going going goroutine : 2 done ``` ## Channels *Channels* are the pipes that connect concurrent goroutines. You can send values into channels from one goroutine and receive those values into another goroutine. Create a channel: ```go ch := make(chan int) ``` Send and receive values to/from channel: ```go ch <- v // Send v to channel ch. v := <-ch // Receive from ch, and assign value to v. ``` Example: distributed sum of array: ```go package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // send sum to c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // receive from c fmt.Println(x, y, x+y) // -5 17 12 } ``` ## Buffered channel Channels can be buffered: ```go ch := make(chan int, 100) ``` Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty. Dead-lock example: ```go func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 ch <- 3 fmt.Println(<-ch) fmt.Println(<-ch) } ``` Working: ```go func main() { ch := make(chan int, 2) ch <- 1 fmt.Println(<-ch) ch <- 2 ch <- 3 fmt.Println(<-ch) fmt.Println(<-ch) } ``` ## Channel synchronization We can use channels to synchronize execution across goroutines. Here’s an example of using a blocking receive to wait for a goroutine to finish. ```go package main import ( "fmt" "time" ) func worker(done chan bool) { fmt.Print("working...") time.Sleep(time.Second) fmt.Println("done") done <- true } func main() { done := make(chan bool, 1) go worker(done) // Block until we receive a notification // from the worker on the channel. // without this, nothing will be printed out <-done } ``` ## Range & close a channel A sender can close a channel to indicate that no more values will be sent. The loop `for i := range c` receives values from the channel repeatedly until it is closed. Receivers can test whether a channel has been closed: ```go v, ok := <-ch ``` Sample: ```go package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Print(i, " ") } } // Output: 0 1 1 2 3 5 8 13 21 34 ``` ## Channel direction ```go package main import "fmt" // pings is used for sending only func ping(pings chan<- string, msg string) { pings <- msg } // pings is used for receiving, pongs is for sending func pong(pings <-chan string, pongs chan<- string) { msg := <-pings pongs <- msg } func main() { pings := make(chan string, 1) pongs := make(chan string, 1) ping(pings, "passed message") pong(pings, pongs) fmt.Println(<-pongs) } ``` ## Select The `select` statement lets a goroutine wait on multiple communication operations. A `select` **blocks** until **one** of its cases can run, then it executes that case. It chooses one at random if multiple are ready. ```go package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Print(<-c, " ") } quit <- 0 }() fibonacci(c, quit) } // Output: 0 1 1 2 3 5 8 13 21 34 quit ``` The default case in a select is run if no other case is ready. ```go package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } } ``` # GRPC ## Go - GRPC https://developers.google.com/protocol-buffers/docs/gotutorial ## GRPC https://developers.google.com/protocol-buffers/docs/overview https://developers.google.com/protocol-buffers/docs/proto3 # Awesome Go ### gocloud.dev - [The Go Cloud Development Kit](https://gocloud.dev/) # Other tools ### [direnv](https://direnv.net/) Load and unload environment variables depending on the current directory (from `.envrc` file). - Install: ```shell curl -sfL https://direnv.net/install.sh | bash ``` - Hook into the `bash` shell (for more shells, see [here](https://direnv.net/docs/hook.html)): add the following line at the end of the `~/.bashrc` file: ```shell eval "$(direnv hook bash)" ``` - Alllow direnv: ```shell direnv allow ```