The test runner of the Go track has been updated to Go 1.22. That means that every time you submit a solution via the CLI or using the online editor, it will be ran by a Go 1.22 compiler.
This means you can use Go 1.22 features in the Go track now.
You can read the release notes here: Go 1.22 Release Notes - The Go Programming Language
In this post I’ll explore the most relevant features for the track and how you can use them.
Language features
The main language changes are around for loops.
To use these language changes in Exercism, you must update the go version in the go.mod
of the exercises.
This is because while the Go 1.22 compiler is able to understand the new syntax, these new features are only activated in a Go module if they declare a Go version >= 1.22 in their go.mod
file.
Currently in the track, the go.mod
files declare Go 1.18. This means to use these new language features, you must update this to 1.22 and submit the modified go.mod
file along your solution files. Unfortunately, there’s currently no way to do this if you are using the online editor.
Loop Variables are created in each iteration
Before, when a variable was created in a declaration of a for loop, the compiler under the hoods would create the variable once, and update its value in each iteration. The behavior was changed for the variable to be created in each iteration of the loop.
For instance, take this simple loop that creates 5 goroutines and prints the loop variable inside it:
for i := 0; i < 5; i++ {
go func() {
fmt.Println(i)
}()
}
The results of running this in Go 1.21 vary on the run, but a very common result was to get:
5
5
5
5
5
This happened because all the goroutines created by the loop would get a reference to i
. But before they could get to start running, the loop would finish and set i
to 5. When all the goroutines would run, they all would see i
set to 5
, so all print this value.
In Go 1.22 this doesn’t happen anymore, and the program above prints something like:
0
4
1
2
3
And we get all the values of i
printed, like one would expect.
In Go 1.21, to get around this, there were 2 common options.
The first was to redeclare the variable inside the loop to force a new variable to be created, with a copy of i
:
for i := 0; i < 5; i++ {
i := i
go func() {
fmt.Println(i)
}()
}
You can think of this as being what Go does by default now, but without the need of having to write something like i := i
.
Another option that would do the same of producing a copy of the variable was to make the goroutine take a parameter and pass the loop variable as argument:
for i := 0; i < 5; i++ {
go func(inner_i int) {
fmt.Println(inner_i)
}(i)
}
But these tricks are no longer needed!
You can read more about the motivation for this change here: Fixing For Loops in Go 1.22 - The Go Programming Language
For loops now can range over integers
For loops can now range over integers! This is super handy when creating loops that we want to go from 0 to a specific value:
for i := range 10 {
fmt.Println(10 - i)
}
fmt.Println("lift-off!")
It also means the loop in the example of the previous section could also be written as:
for i := range 5 {
go func() {
fmt.Println(i)
}()
}
In this last example, we are using both language changes of Go 1.22 at the same time!
Standard Library changes
The standard library also saw some interesting changes in this release.
Contrary to what happens with language features, you can take advantage of the new standard library in Exercism without any changes to the go.mod
file!
This means it’s perfectly fine to use things in the standard library of 1.22 with go.mod
files declaring a Go version that is less than 1.22. As long as you are running Go 1.22, like the test runner now is, you should be fine.
New math/rand/v2
package
There’s a new default package for randomness in Go, /math/rand/v2 that aims to be easier to use and faster. Most notably:
-
/math/rand
becomes the first standard library package to have a v2! -
Two pseudo-random generator sources: ChaCha8 and PCG. Go uses ChaCha8 for the global generator.
-
Many methods now use faster algorithms
-
In the previous
math/rand
package it was possible to specify a seed for the global generator. This is not possible in the new version of the package.
Improvements to the slices
package
Go 1.21 introduced the slices package, which provides generic functions for slices that perform common operations on slices.
In Go 1.22, this package gets a new function slices.Concat() to concatenate any number of slices.
Go 1.22 in Exercism
Try out these features!
We encourage you to try out these features in Exercism! A few pointers where to look:
-
If you solved Parallel Letter Frequency, it’s very likely you’ve used one of the tricks presented above to copy a loop variable into a goroutine function. Try changing that exercise to take advantage of the new behavior of loop variables!
-
To experiment with the range over integers, look for solutions where you are doing a simple loop that starts with the loop variable and increments (or decrements) that value by 1 in each iteration. Try converting those loops into a range over integers. A good way to start is to search all you solutions by “for i := 0” and see what comes up!
-
See if there are exercises where you are doing operations on slices that can now benefit with the use of the slices or maps package. A good place to start is the Card Tricks exercise!
Reminder: to try out the new features that are language changes (like the changes to for loops), you do need to change and submit your go.mod
file with Go 1.22 specified. To use the new things in the standard library, you don’t need to modify the go.mod
file.
Changes in exercises
With the new math/rand/v2 package, some functionality of the previous version of the package was deprecated, most notably, the ability to seed the global generator with rand.Seed()
.
In Animal Magic one of the tasks requires you to use rand.Seed()
. With this function now being deprecated, we’ll change this exercise to not use this function anymore. In a more holistic way, it’s probably a good idea to rethink the exercise to either use math/rand/v2
or ideally, be designed in a way that allows for both math/rand/v2
or math/rand
to be used equally easily.