Created
December 15, 2019 01:26
-
-
Save zcaceres/db2d99f23da4ec9befcb60465f6ad29a to your computer and use it in GitHub Desktop.
Revisions
-
zcaceres created this gist
Dec 15, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,405 @@ # Go: The Weird Parts Notes on the unique stuff about go, in the context of other languages that I already know. ## Project Organization 1. Root dir `go` 2. `src` and `bin` inside the project 3. unique project/package name > The first statement in a go source file must be `package packagenamehere` ## Weird Stuff Most stuff feels overall quite C-like. ### Slices Slices are a bit different. Constrained by `type` of their content, but not by length as a standard `array`. Create an empty slice with non-zero length, use `make`. Make a slice of length 3 (zero-valued). ``` s := make([]string, 3) ``` `append` returns a new slice with 1 or more values. Immutable, which is nice compared to Javascript's array.append. ``` s = append(s, "d") s = append(s, "e") ``` `slice` operator similar to Python. ``` s[1:3] ``` Initialize values for a slice ``` t = []string{"go", "is", "cool"} ``` ### Maps Similar to a dict or hash table. String to int map. ``` m := map(map[string]int) ``` delete values with: ``` delete(m, "mykey") ``` optional second return value indicates whether the key was present in the object ``` value, didKeyExist = m["mykey] ``` ### Range Sort of a mapper function for various data structure. Or a `for in`. ``` nums := []int{2 ,3 4} sum := 0 for _, num := range nums { sum += nums } kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } // byte index, char (rune) for i, c := range "go" { fmt.Println(i, c) } ``` ### Multiple Return Values A bit like python tuples. ``` func inty(a, b int) (int, int) { return a, b } ``` ### Variadic Functions Just like the ...rest operator in JS. ``` func spready(nums ...int) int { total := 0 for _, num := range nums { total += num } return total } ``` ### Closures Go supports anonymous functions, just like Javascript. ``` func factory() func() int { i := 0 return func() int { i++ return i } } ``` ### Pointers Pointers like C. ``` func passedByValue(value int) { # value is passed as a copy of the underlying value and here we operate on that copy only value = 0 } func passedByReference(reference *int) { # here we mutate the underlying value by assigning a new int at the referenced address *reference = 0 } ``` Underlying memory addresses: ``` func main() { i := 0 fmt.Println(i) passedByValue(i) fmt.Println(i) passedByReference(&i) fmt.Println(i) fmt.Println(&i) } ``` ### Structs Like C# structs. ``` type person { name string age int } func NewPerson(name string) *person { p := person{name: name} p.age = 42 return &p } func main() { This syntax creates a new struct. 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) } ``` It's idiomatic to initiate a new struct with a factory function. You can also add methods on structs. ``` type rect struct { width, height int } // Pointer receiver type func (r *rect) area() int { return r.width * r.height } // Value receiver type func (r rect) perim() int { return 2*r.width + 2*r.height } ``` Go seems to know that the method is on the struct since the struct is a parameter. The method is named in the function call defined at the top i.e. `area()`. Go also magically converts between values and pointers for method calls. You can control the behavior by specifying a pointer receiver type to avoid copying the struct on method calls or to allow the method to mutate the underlying values. ### Interfaces A "named collection of method signatures". ``` 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 } // Value receiver type 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.area()) fmt.Println(g.perim()) } ``` ### Errors Go communicates errors with explicit return values. Different than exceptions in other languages. ``` import ( "errors" ) func errorfunc(arg int) (int, error) { if arg == 42 { return -1, errors.new("An unholy number") } return arg + 3, nil } func main() { if r, e := errorFunc(42); e != nil { // FAIL } else { // OK } } ``` ### Goroutines Goroutine is "a lightweight thread of execution". Sort of like a Javascript promise except actually concurrent (since JS is forever single-threaded). ``` func f(arg int) int { return arg } f(4) // called synchronously go f(5) // called asynchronously // with an anonymous function go func(msg string) { fmt.Println(msg) }("GOING ASYNC ANON") ``` ### Timers `timer` Basically setTimeout from JS. ``` import "time" function timeMe() { // returns a channel that will be notified at that time (wait two seconds) timer1 := time.NewTimer(2 * time.Second) timer1.Stop() // cancel timer // sleep time.Sleep() } ``` `ticker` Basically setInterval from JS ``` package main import ( "fmt" "time" ) func main() { ticker := time.NewTicker(500 * time.Millisecond) done := make(chan bool) go func() { for { select { case <-done: return case t := <-ticker.C: fmt.Println("Tick at", t) } } }() time.Sleep(1600 * time.Millisecond) ticker.Stop() done <- true fmt.Println("Ticker stopped") } ``` ### Panic Sort of like `throw` from JS but it will throw a non-zero exit code and provide a stack trace to stderr. ``` package main import "os" func main() { panic("a problem") _, err := os.Create("tmp/file") if err != nil { panic(err) } } ``` ### Defer Like a `finally` in JS. Except you defer a function call. You have to check for errors even in a deferred function. For example, you `defer` the cleanup of a file. ``` func main() { f := createFile("/tmp/defer.txt") defer closeFile(f) writeFile(f) } ``` ### Exit `os.Exit(3)` to exit with an explicit exit code. Return values from main don't count a la C.