rss resume / curriculum vitae linkedin linkedin gitlab github twitter mastodon instagram
What is new in Go 1.22?
Feb 12, 2024

Disclaimer: This post includes Amazon affiliate links. Clicking on them earns me a commission and does not affect the final price.

Hello πŸ‘‹ It’s that time of the year again! Another minor version of Go was released, superseding Go 1.21, and released a few days ago on February 6th; this is Go 1.22! πŸ’₯ πŸŽ‰ 🎊

Updating

Depending on the platforms you’re using for development and production, you may already be able to use Go 1.22; if not, the official download page provides pre-packaged versions for different platforms.

For more concrete examples:



What is new?

Go 1.22 supports a lot of nice new changes. In this post, I mention six updates worth calling out; however, consider reading the full release notes to get familiar with other changes that could be relevant to your existing use cases. I’m sure you will find something interesting.

Fix the for gotcha

Code for this example is available on Github.

One of the most common errors when working with closures and goroutines in a for loop is how the variables declared by a for are used and shared. This new change creates new variables behind the scenes, allowing the values to be shared by the internal calls properly.

For example, the following code:

 6	done := make(chan bool)
 7
 8	values := []string{"a", "b", "c"}
 9	for _, v := range values {
10		// go 1.21 requires:
11		// v := v
12		go func() {
13			fmt.Println(v)
14			done <- true
15		}()
16	}
17
18	// wait for all goroutines to complete before exiting
19	for range values {
20		<-done
21	}

Will print correctly the three values in any order, while the previous version will print the last value all the time.

Support to range over integers in for

Code for this example is available on Github.

This new change to the language also updates the for behavior. It allows using integers to non-inclusive iterate over them. For example:

6	for i := range 5 {
7		fmt.Println(i)
8	}

Will print out

0
1
2
3
4

First ever v2 package: math/rand/v2

Code for this example is available on Github.

This new package brings improvements over the original v1 implementation, such as performance, idiomatic naming for functions, and a new generic function to generate random values from any integer type, to mention a few.

For example:

 6	msgs := []string{
 7		"one",
 8		"two",
 9		"three",
10		"four",
11	}
12
13	fmt.Println("number is:", msgs[rand.IntN(len(msgs))])
14
15	// IntXYZ are idiomatic, for example:
16	// math/rand/Int31 -> math/rand/v2/Int32
17
18	fmt.Println(rand.Int32())
19	fmt.Println(rand.Int64())
20
21	// rand.N(5*time.Minute)
22
23	fmt.Println(rand.N(10 * time.Second))

Another essential thing to note is that the Go team plans to provide a tool to allow migration from existing v1 to this new package in future releases.

Enhanced routing patterns in net/http.ServeMux

Code for this example is available on Github.

One of the most popular new features included in this release is the support to natively handle routes using patterns, similar to what other third-party packages provide (such as gorilla-mux, for example).

Take the following piece of code:

21	mux.HandleFunc("GET /{id}", func(w http.ResponseWriter, req *http.Request) {
22		id := req.PathValue("id")
23		fmt.Fprintf(w, "Your id is: %v\n", id)
24	})
25
26	mux.HandleFunc("GET /{id}/value", func(w http.ResponseWriter, req *http.Request) {
27		id := req.PathValue("id")
28		fmt.Fprintf(w, "Your id is: %v\n", id)
29	})

The method is explicitly indicated, in this case, GET. Also, you can match segments of the URL path, so in the example above, we are matching {id} but for two different paths.

One crucial feature in this new implementation is the early detection of routes that conflict, so enabling the following code will panic during runtime:

31	// XXX: Causes a panic
32	// mux.HandleFunc("GET /val1/{val2}", func(w http.ResponseWriter, req *http.Request) {
33	// 	val2 := req.PathValue("val2")
34	// 	fmt.Fprintf(w, "Your child id is: %v\n", val2)
35	// })

The panic happens because GET /val1/{val2} and GET /{id}/value conflict with each other:

panic: pattern "GET /val1/{val2}" (registered at main.go:32) conflicts with pattern "GET /{id}/value" (registered at main.go:26):
GET /val1/{val2} and GET /{id}/value both match some paths, like "/val1/value".
But neither is more specific than the other.
GET /val1/{val2} matches "/val1/val2", but GET /{id}/value doesn't.
GET /{id}/value matches "/id/value", but GET /val1/{val2} doesn't.

New slices.Concat` function

Code for this example is available on Github.

Concat is a new generic function that allows concatenating multiple slices of the same type, and it pre-allocates the final length to avoid reallocations when appending.

For example, the following code:

 9	s1 := []string{"one", "two"}
10	s2 := []string{"three"}
11	s3 := []string{"four"}
12
13	res := slices.Concat[[]string](s1, s2, s3)
14
15	fmt.Printf("slices: %#v\n", res)

Prints out the following:

slices: []string{"one", "two", "three", "four"}

Conclusion

As I mentioned initially, there are more features than I can list in this post; please read the full release notes. You will find something useful. The best new feature is the enhanced routing patterns, which allows me to reduce third-party dependencies, which I enjoy.

Great job Go team, I’m looking forward to Go 1.23!

If you’re looking to sink your teeth into more Go-related topics I recommend the following books:


Back to posts