index.md (8974B)
1 --- 2 title: "Golang" 3 date: 2023-05-08T09:54:48-04:00 4 draft: false 5 references: 6 - title: "Why David Yach Loves Go" 7 url: https://cloud.google.com/blog/products/application-modernization/why-david-yach-loves-go 8 date: 2023-06-13T14:51:05Z 9 file: cloud-google-com-windxx.txt 10 - title: "One process programming notes (with Go and SQLite)" 11 url: https://crawshaw.io/blog/one-process-programming-notes 12 date: 2023-06-13T14:49:51Z 13 file: crawshaw-io-k5slfj.txt 14 - title: "Go Project Layout" 15 url: https://medium.com/golang-learn/go-project-layout-e5213cdcfaa2 16 date: 2023-06-13T15:00:02Z 17 file: medium-com-efpmux.txt 18 - title: "Gopher Wrangling. Effective error handling in Go | Stephen's Tech Blog" 19 url: https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/ 20 date: 2023-06-20T16:25:12Z 21 file: stephenn-com-kbiijs.txt 22 - title: "Effective Go - The Go Programming Language" 23 url: https://go.dev/doc/effective_go 24 date: 2023-07-12T03:17:03Z 25 file: go-dev-vfin4x.txt 26 --- 27 28 I find [Go][1] really compelling, even though it's not super applicable to my job. When evaluating a new tool, I find I'm weirdly biased to things written in Go. 29 30 * I like that it compiles, and have no desire to install someone else's Python 31 * It just seems to hit the right balance of productivity, performance, simplicity, safety 32 * The language (and the tech built with the language) just seems built to last 33 34 [1]: https://go.dev/ 35 36 ### Questions to Answer 37 38 * How to organize large(r) codebases 39 * Goroutines / concurrency 40 * Dev tooling 41 * How to read/write JSON 42 * How to validate with JSON Schema 43 * <https://github.com/qri-io/jsonschema> 44 * Testing 45 46 ### Projects I like 47 48 * [Hugo][2] 49 * [Caddy][3] 50 * [PocketBase][4] 51 * [SyncThing][5] 52 * [Restic][6] 53 * [Gotenberg][7] 54 * [Shiori][8] 55 * [Listmonk][9] 56 57 [2]: https://gohugo.io/ 58 [3]: https://caddyserver.com/ 59 [4]: https://pocketbase.io/ 60 [5]: https://syncthing.net/ 61 [6]: https://restic.net/ 62 [7]: https://gotenberg.dev/ 63 [8]: https://github.com/go-shiori/shiori 64 [9]: https://listmonk.app/ 65 66 ### Project Ideas 67 68 * Bookmarking app (Pinboard replacement) 69 * Note-taking / journaling app 70 * [StevieBlocks][10] 71 72 {{<dither project1.png "374x">}}Handwritten notes outlining a Go project plan: build a web service that accepts POST JSON, validates it against a schema, replies with errors if invalid or 200 if valid, and then stores the data in SQLite.{{</dither>}} 73 74 [10]: https://gist.github.com/dce/f975cb21b50a2cf998bf7230cbf89d85 75 76 ### Resources 77 78 * [Standard Go Project Layout][11] 79 * [The files & folders of Go projects][12] 80 * [Why David Yach Loves Go][13] 81 * [One process programming notes (with Go and SQLite)][14] 82 * [Go Project Layout][15] 83 * [Gopher Wrangling. Effective error handling in Go][16] 84 * [Effective Go][17] 85 86 [11]: https://github.com/golang-standards/project-layout 87 [12]: https://changelog.com/gotime/278 88 [13]: https://cloud.google.com/blog/products/application-modernization/why-david-yach-loves-go 89 [14]: https://crawshaw.io/blog/one-process-programming-notes 90 [15]: https://medium.com/golang-learn/go-project-layout-e5213cdcfaa2 91 [16]: https://stephenn.com/2023/06/gopher-wrangling.-effective-error-handling-in-go/ 92 [17]: https://go.dev/doc/effective_go 93 94 ### Notes 95 96 * Regular Expressions 97 * Compile with `regexp.MustCompile` (no need to check for error) 98 * Strings denoted by backticks don't escape; use these for regular expressions 99 * For case-insensitive matching, start the expression with `(?i)` 100 * [Unnamed parameters][18] 101 102 > Unnamed parameters are perfectly valid. The [Parameter declaration](https://golang.org/ref/spec#ParameterDecl) from the spec: 103 > 104 > ``` 105 > ParameterDecl = [ IdentifierList ] [ "..." ] Type . 106 > ``` 107 > 108 > As you can see, the `IdentifierList` (the identifier name or names) is in square brackets, which means it's _optional_. Only the `Type` is required. 109 > 110 > The reason for this is because the names are not really important for someone calling a method or a function. What matters is the types of the parameters and their order. This is detailed in this answer: [Getting method parameter names in Golang](https://stackoverflow.com/questions/31377433/getting-method-parameter-names-in-golang/31377793#31377793) 111 112 * [Named result parameters][19] 113 114 > The return or result "parameters" of a Go function can be given names and used as regular variables, just like the incoming parameters. When named, they are initialized to the zero values for their types when the function begins; if the function executes a return statement with no arguments, the current values of the result parameters are used as the returned values. 115 116 * Type Conversion & Assertion 117 * Built-in functions for conversion (`float64`, `strconv.Atoi`) 118 * `if v, ok := fnb.(FancyNumber); ok {` (`v` is a `FancyNumber` if `ok` is true) 119 * `switch v := i.(type) {` (case per type, `v` is `i` cast to that type) 120 * [Custom error types][20] 121 122 > Usually, a struct is used to create a custom error type. By convention, custom error type names should end with `Error`. Also, it is best to set up the `Error() string` method with a pointer receiver, see this [Stackoverflow comment](https://stackoverflow.com/a/50333850) to learn about the reasoning. Note that this means you need to return a pointer to your custom error otherwise it will not count as `error` because the non-pointer value does not provide the `Error() string` method. 123 124 * [Maps][21] 125 126 > A Go map type looks like this: `map[KeyType]ValueType` 127 128 > To initialize a map, use the built in `make` function: `m = make(map[string]int)` 129 130 > This statement retrieves the value stored under the key `"route"` and assigns it to a new variable i: `i := m["route"]`. If the requested key doesn’t exist, we get the value type’s _zero value_. In this case the value type is `int`, so the zero value is `0`. 131 132 > A two-value assignment tests for the existence of a key: `i, ok := m["route"]` 133 134 * [Insertion order has no effect on the order keys/values are retrieved in a `range` query][22] 135 136 > The iteration order over maps is not specified and is not guaranteed to be the same from one iteration to the next. 137 138 * [`new` allocates zeroed storage for a new item or type whatever and then returns a pointer to it.][23] 139 140 * First-class functions 141 * Go supports first-class functions (functions as arguments and return values of other functions) 142 * Go has closures (so variables defined inside a function that returns a function get closed over) 143 * Go has anonymous functions (`func` w/o name) 144 * Slices 145 * Use `append` to add to a slice; this creates a new slice, so you have to capture the return value (`s = append(s, 100)`) 146 * `math/rand` 147 * `rand.Intn`: random integer between 0 and the specified int 148 * `rand.Float64`: random float between 0.0. and 1.0 149 * `rand.Shuffle`: randomize an array/slice; takes a length and a swap function 150 * Stringers 151 * Define a `String() string` method for types so that `fmt` knows how to stringify them 152 * E.g. `fmt.Sprintf("%s", yourThing)` will call `yourThing`'s `String()` function 153 * Structs 154 * [What Are Golang's Anonymous Structs?][24] 155 156 > An anonymous struct is just like a normal struct, but it is defined without a name and therefore cannot be referenced elsewhere in the code. 157 > 158 > Structs in Go are similar to structs in other languages like C. They have typed collections of fields and are used to group data to make it more manageable for us as programmers. 159 > 160 > To create an anonymous struct, just instantiate the instance immediately using a second pair of brackets after declaring the type. 161 162 * [Time][25] 163 * `time.Duration` -- `time.Second * 1e9` is a duration of one billion seconds 164 * `1e9` is the same as `1.0*math.Pow(10, 9)` 165 * Runes 166 * `unicode.IsLetter` -- test if rune is a letter 167 * `strings.ContainsRune` -- test if string contains letter (`strings.ContainsRune(s[i+1:], c)`) 168 * `log.Println` -- print out anything (like a data structure) 169 * `strings.Map` -- map over a string 170 * `return strings.Map(func(r rune) rune { return mapping[r] }, dna)` 171 * Generics 172 * Use `[]` to define types 173 * `func keep[T int | []int | string](in []T, filter func(T) bool) (out []T) {` 174 * [Use `~` to allow "inheriting" (?) types][26] 175 176 > In Go generics, the ~ tilde token is used in the form ~T to denote the set of types whose underlying type is T. 177 178 * [Type Aliases][27] 179 180 > An alias declaration doesn’t create a new distinct type different from the type it’s created from. It just introduces an alias name T1, an alternate spelling, for the type denoted by T2. 181 182 [18]: https://stackoverflow.com/a/40951013 183 [19]: https://go.dev/doc/effective_go#named-results 184 [20]: https://exercism.org/tracks/go/concepts/errors 185 [21]: https://go.dev/blog/maps 186 [22]: https://go.dev/ref/spec#RangeClause 187 [23]: https://stackoverflow.com/a/34543716 188 [24]: https://blog.boot.dev/golang/anonymous-structs-golang/ 189 [25]: https://pkg.go.dev/time 190 [26]: https://stackoverflow.com/a/70890514 191 [27]: https://yourbasic.org/golang/type-alias/