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

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

Getting the Git Commit in Go

Go 1.18 added a feature to easily get the Git commit version that the binary was built from. This is so much easier than the old way of doing it! 🎉 package main import ( "fmt" "runtime/debug" ) func main() { info, _ := debug.ReadBuildInfo() for _, setting := range info.Settings { if setting.Key == "vcs.revision" || setting.Key == "vcs.time" { fmt.Printf("%s:\t%s\n", setting.Key, setting.Value) } } } $ go build git-version $ ./git-version vcs.revision: 4d47dd39d1debbdf10715fd9ff967e09d4347f02 vcs.time: 2023-09-02T00:21:21Z More info here. 👉 Remember: For this to work, you must build like go build -o ./build/foo. Using go build -o ./build/foo/main.go will not work.

2023-09-01

Waiting for data from a Go channel with a timeout

The other day I ran into a situation where I wanted my function to block until it received data from a Go channel. But I didn’t want it to block indefinitely. I wanted it to timeout after a few seconds if it didn’t get any data. My coworker Dan showed me a technique I didn’t know about to easily accomplish this. It wasn’t surprising to learn how easy it was, given that Go has strong support for concurrency out the gate. ...

2023-09-01

Blocking with Channels in Go

Example 1 In the below code, <-messages will block until it gets something on the channel. This prevents main() from exiting until one item is received on the channel. package main import ( "fmt" "time" ) func waitThenSend(msg chan string) { time.Sleep(2 * time.Second) msg <- "badwolf" } func main() { messages := make(chan string) // make unbuffered channel go func() { waitThenSend(messages) // make goroutine, pass in channel }() <-messages // blocks until channel receives a value fmt.Println("done") } Example 2 The below example demonstrates using a sync.WaitGroupto keep the program running until all of the goroutines have exited. Data is sent to the function via the unbuffered channel named numbers. ...

2023-08-17