How my digital life is backed up

Derek Sivers recently wrote a post explaining how he backups his computers. His post inspired me to improve my own backup strategy and also to write this post. My computer I only have two personal computers whose data I care about; my Macbook Pro and my wife’s Macbook Air. The strategies for backing these up are the same, so I’ll just talk about my computer. It has a lot of important things: files, videos, photos, personal code, and more. I’d be pretty sad and set back if I lost all of it. So I’m pretty exhaustive about my backup strategy, but it ends up taking very little of my personal time because most of it happens with little thought on my part. ...

2024-03-22

Running a subset of Go tests

It is often useful to run a subset of the tests in a Go project. You might do this because you only want to see test results for one package or to run tests faster. For these examples, assume your project is a Go module named examplemodule. It has the following structure: examplemodule |_ go.mod |_ go.sum |_ internal |_ foo | |_ foo.go | |_ foo_test.go |_ bar |_ bar.go |_ bar_test.go Here’s the most useful techniques I use to do this: ...

2024-03-07

Max and min integer values in Golang

Today I needed to use the maximum unsigned 64-bit integer value possible in Golang. Here is a short program I wrote with some help from Stack Overflow to help me remember how to calculate these without any dependencies. package main import "fmt" const ( minUint32 = uint32(0) maxUint32 = ^uint32(0) minUint64 = uint64(0) maxUint64 = ^uint64(0) minInt32 = int32(-maxInt32 - 1) maxInt32 = int32(maxUint32 >> 1) minInt64 = int64(-maxInt64 - 1) maxInt64 = int64(maxUint64 >> 1) ) func details[numeric int32 | int64 | uint32 | uint64](name string, num numeric) { fmt.Printf("%9s -> dec: %21d\tbin: %65b\n", name, num, num) } func main() { details("minUint32", minUint32) details("maxUint32", maxUint32) details("minUint64", minUint64) details("maxUint64", maxUint64) details("minInt32", minInt32) details("maxInt32", maxInt32) details("minInt64", minInt64) details("maxInt64", maxInt64) } Running that shows: ...

2024-03-05

Using 'git commit --fixup'

I learned something new from Julia Evans today. Git has a --fixup argument when committing files which makes it easy to create fixup commits which can get automatically squashed when rebasing. This is fantastic! I’ve known about git commit --amend for years, but this allows you to fixup commits which are several commits back without manually moving a bunch of lines around while interactively rebasing… Let’s assume you have a repo which looks like shown below. 696792e9 is our “bad” commit, which we need to fixup. ...

2024-02-20

Go build tags

Today I learned about Go build tags. Here’s some quick notes to help me remember how to use them. Assume you have directory like so: $ ls -1 extra.go go.mod main.go And main.go has contents: package main import "fmt" var numbers = []string{ "one", "two", } func main() { for _, number := range numbers { fmt.Println(number) } } And extra.go has contents: //go:build extrastuff package main func init() { numbers = append(numbers, "three", "four") } If you build without any tags, you get this: ...

2024-01-03

Using gonew to easily create template repos

Now that I’ve been writing Golang for a while, when I start a new project, I typically know the sort of layout I’m looking for. I typically go for something like this: . ├── cmd │ └── demo │ └── main.go ├── go.mod ├── internal │ └── subpkg │ ├── subpkg.go │ └── subpkg_test.go └── LICENSE And often there are lots of ascillary files that go along with this, like Makefiles, CI/CD config files, Dockerfiles, docker-compose files, etc. ...

2023-12-14

Checking an error's type in Golang

I was not very familiar with checking an error’s type in Golang, so I spent a few minutes learning about it today. It turns out that it’s incredibly easy to do. Running the below code shows the output: 2009/11/10 23:00:00 got custom error: err1 package main import ( "errors" "log" ) var ( customErr = errors.New("err1") // create a error, identified by its var name ) // oops always returns our custom error. Oops! func oops() error { return customErr } func main() { err := oops() if errors.Is(err, customErr) { // check error type with errors.Is() log.Fatal("got custom error: ", err) } } This is doing more than just matching the string of the error, as this does not show any output: ...

2023-12-07

Benchmarking Unnecessary Allocations

I’ve also been thinking more about unnecessary allocations in my Go code and how to avoid them by pre-declaring the length of a slice up front. Normally, I’d write something like this: var s []int for _, val := otherSlice { s = append(s, val) } Since I don’t specify the size of s, if otherSlice is large, the array underlying s might not be large enough to hold all the values; then a new array will have to be allocated and (I presume) all existing values copied out of it one at a time to fill the new array. ...

2023-09-30

Templating files with Golang

I recently went through How To Use Templates in Go to refresh my memory on Golang templates. I was reminded how great they are and learned several things along the way. I learned that the below syntaxes are equivalent: {{ . | len }} {{ (len .) }} Here is the program I wrote and tweaked along the way, to refresh my memory in the future: package main import ( "html/template" "os" "strings" ) type Pet struct { Name string Sex string Intact bool Age string Breed []string } var dogs = []Pet{ { // This is why you should use html/template and not text/template when // rendering HTML. Especially if you don't trust the source of your // data! // // You don't want the template you render to do (potentially) malicious // things in your user's webbrowser. Name: "<script>alert(\"Gotcha!\");</script>Jujube", Sex: "Female", Intact: false, Age: "10 months", Breed: []string{ "German Shepherd", "Pitbull", }, }, { Name: "Zephyr", Sex: "Male", Intact: true, Age: "13 years, 3 months", Breed: []string{ "German Shepherd", "Border Collie", }, }, { Name: "Roger", Sex: "Male", Intact: false, Age: "9 years, 7 months", Breed: []string{ "German Shepherd", "Border Collie", }, }, { Name: "Missi", Sex: "Female", Intact: false, Age: "99 years, 2 months", Breed: []string{ "Teacup Poodle", }, }, } // This is how you add functions to your template. You can add functions from // other packages or define them inline here. // // See petsHtml.tmpl for an example of using "join". var funcMap = template.FuncMap{ "dec": func(i int) int { return i - 1 }, // Inline! "replace": strings.ReplaceAll, // Other package! "join": strings.Join, } func main() { // Create Template, add functions, and load template files. tmpl, err := template.New("").Funcs(funcMap).ParseGlob("*.tmpl") if err != nil { panic(err) } // Create a file. var f *os.File f, err = os.Create("pets.html") if err != nil { panic(err) } defer f.Close() // Execute a template by name with data "dogs" and write to writer "f". err = tmpl.ExecuteTemplate(f, "petsHtml.tmpl", dogs) if err != nil { panic(err) } } And here are the files I wrote and tweaked along the way: ...

2023-09-19

Goroutines outlive their calling function

While watching a talk by Rob Pike I learned today something about Goroutines which surprised me: Goroutines outlive their calling function. Said another way, if the function which created the goroutine returns, the goroutine will continue running. (main() is the one exception.) This is fantastic! 🎉 Here’s an example of this in practice. package main import ( "fmt" "time" ) func person(msg string) <-chan string { // Function returns a receive-only channel ch := make(chan string) // Create unbuffered channel go func() { // This goroutine lives on after person() returns for i := 0; ; i++ { time.Sleep(time.Duration(rand.Intn(1e3)) * time.Millisecond) ch <- fmt.Sprintf("%s %d", msg, i) } }() return ch } func main() { james := person("james") // Assign returned channel to variable sinah := person("sinah") for i := 0; i < 5; i++ { fmt.Println(<-james) // Block, waiting for value on channel fmt.Println(<-sinah) } fmt.Println("Done!") } Output: ...

2023-09-18