Go sub-slice gotchas

Thanks to Julia Evan’s latest post, I learned that creating new slices by sub-slicing an existing slice has an important caveat: They can sometimes use the same backing array! 😬 This is important to understand if you mutate the sub-slice. Take the below example, wherein we accidentally mutate s1! package main import ( "fmt" ) func main() { s1 := []int{0, 1, 2, 3, 4, 5} // len == 6, capacity == 6 s2 := s1[1:5] // len == 4, capacity == 5 // Modifies both s1 and s2, because they share the same backing array....

2024-08-11

Configuring a Git pre-push hook to run unit tests

A coworker turned me onto this lovely technique the other day. You can use a git pre-push hook to run all of your Golang unit tests before pushing. To do this, make a the following file: $YOUR_REPO/.git/hooks/pre-push The file must be executable. The file’s contents should be: #!/bin/sh if ! go test ./... ; then echo echo "Rejecting commit. Unit tests failed." echo exit 1 fi Easy peasy.

2024-06-26

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....

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....

2024-03-05

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....

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....

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....

2023-09-18