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

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

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

2023-08-17

nil maps in Golang

I’ve been a bit confused for a while over when a map in Golang gets created with a value of nil (its zero value) and when it does not, so I’m writing this to help me remember. Let’s look at various ways to initialize a map: package main import "fmt" func main() { m1 := map[string]string{} // initializes map m2 := make(map[string]string) // also initializes map var m3 map[string]string // does NOT initialize the map!...

2023-01-07

Built-in Go HTTP server

Go has a built-in HTTP server in net/http. Here’s me playing around using it: package main import ( "fmt" "net/http" "strings" ) func hello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") } func details(w http.ResponseWriter, req *http.Request) { resp := []string{} resp = append(resp, "Request Details:") resp = append(resp, fmt.Sprintf("- Request proto: %s", req.Proto)) resp = append(resp, fmt.Sprintf("- Headers:")) for name, val := range req.Header { resp = append(resp, fmt.Sprintf("\t- %s: %s", name, val)) } resp = append(resp, "\n") fmt....

2022-10-14

Fixing: File is not `goimports`-ed (goimports)

The other day, I was linting my Go code with golangci-lint when I got this error: File is not `goimports`-ed (goimports) I examined the file and found nothing amiss, but the linter insisted something was wrong. Eventually, I realized I had used spaces to indent the file rather than tabs. Changing the indent character to tabs fixed it. I then ran into the same error on a second file. This time, I had a comment which was wrong....

2022-10-05

Go and pass by value

Go normally uses pass-by-value for function calls. When you pass a variable into a function or method, Go will (under the hood) create a new variable, copy the old variable’s value into it, and then pass the new variable (not the original variable) into the function or method. Non-pointer values These types behave as described above and are sometimes called non-pointer values: Strings Ints Floats Booleans Arrays Structs Here’s an example of how these work....

2022-09-01

Constants in Go

Constants syntax I always have a hard time remembering the syntax of declaring constants in Go. So here’s some reminders to myself. It is possible to declare constants one per line like you’d expect. Note that the there are typed and untyped constants as shown here below. package main import "fmt" const vermKnid string = "scram" const fox = "foxes likes loxes" func main() { fmt.Println(vermKnid) fmt.Println(fox) } But you can also use a constant declaration group, which does the same thing but is easier to read if you’re declaring a bunch of constants....

2022-08-16

Mutexes and concurrent access in Go

Golang has mutexes (short for mutually exclusion) to manage concurrent access to a shared object via multiple goroutines. Here’s an example (taken from the excellent Go by Example: Mutexes). package main import ( "fmt" "sync" ) type Counter struct { mu sync.Mutex count map[string]int } func (c *Counter) Inc(name string) { c.mu.Lock() defer c.mu.Unlock() c.count[name]++ } func main() { cnt := Counter{count: map[string]int{"james": 0, "spartacus": 0}} var wg sync.WaitGroup increment := func(name string, n int) { for i := 0 ; i < n ; i++ { cnt....

2022-07-22