Golang 1.24 is looking seriously awesome
The Golang team just released the first release candidate of Golang 1.24. At Upsun, we use Golang extensively for everything in our hot path, especially for the routing of HTTP requests throughout our infrastructure. Here is what we are looking forward to the most.
Some nice performance improvements
We have not looked into these very closely yet, but they seem nice:
- 2-3% performance improvement across the board, driven by “a new builtin map implementation based on Swiss Tables, more efficient memory allocation of small objects, and a new runtime-internal mutex implementation”1; and
- Two new cgo directives (
noescape
andnocallback
)2 that have the potential to reduce the overhead of calling C code from Go in some cases
A weak pointer implementation (finally!)
Probably my favorite, Go 1.24 includes an implementation of a weak pointer.
A weak pointer is a reference to an object that does not prevent the garbage collector from collecting it. In practice, the implementation is simply a weak.Pointer[T]
(which nicely mirrors atomic.Pointer[T]
), which has a Value() *T
method.
This new weak pointer concept – in addition to the unique
package introduced in Go 1.23 – has the potential of powering nice improvement of memory usage in real-world Go applications.
Post-quantum cryptography
This release introduces support for post-quantum cryptography in the crypto/tls
package. The standard library now includes ML-KEM, “a post-quantum key exchange mechanism formerly known as Kyber and specified in FIPS 203” 3.
Post-quantum cryptography, if you haven’t been following, is a reference to a new set of cryptographic algorithms that are deemed resistant to attacks by quantum computers, which could potentially break many of the current commonly used cryptographic algorithms.
Encrypted Client Hello (ECH) in crypto/tls
The goal of Encrypted Client Hello (ECH), and of its predecessor Encrypted SNI (ESNI) is to bring a long standing privacy gap in TLS: while the actual communication over TLS is private, the identity of the server isn’t. The protocol uses a public key, distributed via DNS and obtained using DNS-over-HTTPS, for encryption during the client’s first flight.
This is an option we are going to look into offering on Platform.sh and Upsun at one point. We still need to figure out how to best do that.
encoding/json
gets better support for zero value through the new omitzero
tag
This release fixes a long-standing issue in encoding/json
: the omitempty
tag only supports a subset of types, and has a definition that is at odd with the standard definition of the zero value in the language. The new omitzero
tag now allows you to specify that a field should be omitted from the serialization if it has the zero value for its type.
The current omitempty
option specifies that the field should be omitted from the encoding “if the field has an empty value, defined as false
, 0
, a nil
pointer, a nil
interface value, and any empty array, slice, map, or string”. This has always been unfortunate, because Golang has different definition of what a “zero value” is: "false
for booleans, 0
for numeric types, ""
for strings, and nil
for pointers, functions, interfaces, slices, channels, and maps".
If you compare the two definitions, you would note they are identical for booleans, all numeric types, empty arrays and interface values. But they handle slices and maps differently:
omitempty
will omit any empty slices or maps (i.e. slices or maps that satisfylen(S) == 0
), which includes slices or maps that arenil
omitzero
on the other hand will only omit slices or maps that arenil
They also handle non-pointer struct values differently. Take this example:
package main
import (
"encoding/json"
"log"
)
type Foo struct {
OmitEmpty Bar `json:"omitempty,omitempty"`
OmitZero Bar `json:"omitzero,omitzero"`
}
type Bar struct {
A int `json:"a"`
}
func main() {
data, _ := json.Marshal(&Foo{})
log.Printf("Generates: %s", string(data))
}
It generates:
{"omitempty": {"a": 0}}
Given the above, our recommendation would be to continue using omitempty
for maps and slices, but use omitzero
for everything else.
A new tool
directive in go.mod
This release adds a new tool
directive to go.mod
, allowing you to specify dependencies to build tools without having to resort to the current convention of having an empty tools.go
somewhere in your project.
Previously, a project depending on the stringer
tool to generate string versions of enums would have somewhere a file containing something like:
//go:build ignore
package foo
import (
_ "golang.org/x/tools/cmd/stringer"
)
And you would execute that tool with a go:generate
line like this:
//go:generate go run golang.org/x/tools/cmd/stringer -type Foo
It worked well enough. But now in Golang 1.24, you can simply add a tool
directive to your go.mod
, like this:
tool golang.org/x/tools/cmd/stringer
This tool becomes available by just running:
//go:generate go tool stringer -type Foo
Not a huge deal, but still a nice quality of life improvement.
Context support in the testing
package
I have been wanting this for a while. The *testing.T
and *testing.B
types now have a Context() context.Context
method, returning a context that is canceled just before Cleanup
functions are called.
This will allow replacing:
func TestFoo(t *testing.T) {
ctx, cancelFn := context.WithCancel(context.Background())
t.Cleanup(cancelFn)
// [...]
}
with:
func TestFoo(t *testing.T) {
ctx := t.Context()
// [...]
}
Another nice quality of life improvement, and the potential to make it easier and more natural to build cleaner tests.
A new testing/synctest
package for testing concurrent code
This is still experimental, but looks really fun. It is a new package that allows you to define “bubbles” of goroutines into which a fake clock is provided (i.e. time.Now()
returns something different). This will eventually make it easier to test time-sensitive concurrent code, where the best practice right now is to provide a clock function returning the current time (i.e. a func() time.Time
), so that fake time can be injected during tests (it is, for example, how the github.com/juju/ratelimit
token-bucket implementation is tested).
A lot of other things
The Golang team really gave us a nice present this holiday season. There is so much more to mention:
- An improved finalizer support with
runtime.AddCleanup
; - The new
encoding.TextAppender
andencoding.BinaryAppender
interfaces, and their support on many types throughout the standard library; - Cryptographically secure random text strings with the new
rand.Text
function; - … and so much more.
Read the full draft release notes, and don’t hesitate to come and discuss your favorite parts with us on Discord!
Runtime, Go 1.24 Release Notes. ↩︎
Tools, Cgo; Go 1.24 Release Notes. ↩︎
New crypto/mlkem package; Go 1.24 Release Notes. ↩︎