Exciting Go Update - v 1.22 Change Log With Examples
Go version 1.22 is out and it has some amazing changes. In this article, we take a look at them!
by Percy Bolmér, January 29, 2024
Go made a change in my life as a developer. It is a language that resonates with me, especially the ideology of keeping things simple.
All Images in this article are made by Percy Bolmér. Gopher by Takuya Ueda, Original Go Gopher by Renée French (CC BY 3.0)
When developing in Go everything feels easy and uncomplex, and allows me to maintain a high development speed.
Not only is the language easy. It also has good development speed, and it is simple to understand. There is also the fact that Go is performant even though having a garbage collector.
And now, with the release of V1.22, we get some amazing, long-awaited fixes!
Let’s stop beating around the bush, and talk about the elephant.
If you still haven’t updated, make sure to use GoTip to update your version easily to the latest and greatest!
Fixing the “Broken” For loop - One of the most common bugs
The for
loop in Go hasn’t been broken, but it is safe to say that the implementation has confused a lot of developers. This confusion has been causing MANY bugs in the history of Go.
Actually Lets Encrypt needed to revoke 3 million certificates due to this bug
Let us take a look at a simple code snippet that does not do what you might expect.
You can try the code in Go Playground if you use versions lower than 1.22.
You would think that this would print all three names in the list, but if we inspect the code closely and think about what is happening. The variable v is being referenced in the goroutine, but each iteration of the for loop will change what v points towards.
Simply put, the reason is that the for loop queues the Goroutines in the scheduler to execute. Before the Goroutines are executed, the next iteration has proceeded and overwritten the variable v.
In Go 1.22, the for
loops will instead handle variables differently and avoid overriding them each time.
If we execute the same code in Go 1.22 instead, it will print all the names.
To ensure compatibility between programs, the Go team solved it by making sure projects using versions before v1.21 will not attempt to compile code that uses 1.22 or forward.
This change is MAJOR, and will prevent a ton of bugs.
Better HTTP Routing In Stdlib
In Go, the std lib goes a long way. I feel that it is true for most cases, but the HTTP library is often replaced due to the lack of built-in ability to handle routing patterns and method declarations.
What this means is that the http.ServeMux
does only accepts a regular path, without parameters and the HTTP methods that are allowed.
Let us view the differences between the old and new ways of handling these things.
Method-Sepcific Routing
Here is an example of an HTTP endpoint, that only accepts HTTP GET methods.
While this is still very little code for setting up an HTTP endpoint, it is mostly boilerplate to only allow GET requests on the endpoint.
Try sending curl requests to the endpoint to ensure it works as expected.
In Go 1.22 the ServeMux
is changed to allow method declarations inside the route path.
That means we can set a route as GET /hello
and it will automatically only accept GET requests and return HTTP 405 otherwise.
Much better and smoother. Only ONE method is accepted, you cannot do GET POST /hello
. Try the CURL command from before and it should have the same behavior.
Wildcards in Patterns
The new routing rules allow us to accept parameters in the path. Path parameters are not uncommon, the fact that we do not have support for them has been a big reason why many people use Gorilla or other libraries.
A path parameter is a part or section of the URL that expects a value in the request, say that we want to allow the users to add a name to say hello to. The request would then be /hello/$NAME
.
The old way to manage this would be like this.
As you can see, that is pretty messy and this is only one variable.
The new ServeMux allows us to specify parameters with a name by wrapping them in {}
. So we can add a parameter for the name by having the route be /hello/{name}
.
To be able to grab the parameter, the HTTP request contains a function called PathValue
. This function accepts the name of the parameter to fetch.
This is the new way of using path variables.
We don’t need to verify that the name is present, if you try to send a request without the parameter you will see that it returns HTTP 404 Not found.
Try curling localhost:8080/hello/percy
and it should print a nice message.
You can also try curling localhost:8080/hello/percy/123
and see that it fails. There can be use cases where you need to allow dynamic paths to be set and still use parameters. To solve that you can end {}
with ...
. Three dots will make the pattern match any segments after the parameter.
This will allow you to pass a dynamic request with more segments to the parameter.
Matching Exact Patterns with Trailing Slashes
This one I didn’t know, but the HTTP mux matches any route that has the correct prefix. That means if the route is hello/
then any requests to paths that start with hello/
will match. The key here is the /
in the end of the path, which has always told ServeMux to match any prefixes afterward.
Running that will allow us to test it easily.
Send a CURL to localhost:8080/hello/percy
will return the same thing as localhost:8080/hello
.
What if we only want to allow EXACT matches? Well, we can now do that using {$}
at the end of the route. This will tell the Servemux to only route exact matches. It has to be at the end of the path.
After updating the route and restarting the program, we can now only send requests to localhost:8080/hello/
.
Conflict Resolution and Precedence
With these new rules being allowed, we now have a new issue. A request can match TWO routes.
This is solved by having the MOST SPECIFIC route always being selected.
I like this approach because I feel that it makes very much sense.
Honorable Mentions
More things are being changed, but I don’t think they are worth their separate chapters. Instead here is a very short summary of the changes.
- The first V2 package in Go -
math/rand/v2
. Rand is getting a V2, with the removal of theRead
method, and faster algorithms throughout the package. A newrand.N
function that accepts generic parameters to randomly get values, works with durations as well. - Slog gets a
SetLogLoggerLevel
to control the log level more easily. slices
gets a new Concat function that merges slices. Any function in the slice package that shrinks sizes such as Delete will now automatically Zero the elements.
Conclusion
These are the changes I think have been worth mentioning in the newest release of Go.
I do hope you are as thrilled as I am regarding the changes to the HTTP routing. I can’t wait to test out building a new HTMX app and using the STD Lib router for this.
And finally, that nasty devil of for behavior that has caused so many bugs will be forever gone.
If you haven’t already make sure you update to the newest version!
What change are you most excited about?
What is your most missed feature in Go?
Feel free to let me know on any of my social media handles.
Appendix
Go Benchmark - https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/go.html
Garbage Collector - https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)
Go change log - https://tip.golang.org/doc/go1.22
Lets encrypt revoke - https://community.letsencrypt.org/t/revoking-certain-certificates-on-march-4/114864
Go playground - https://go.dev/play/p/LkgkmFMoqTS
If you enjoyed my writing, please support future articles by buying me an Coffee