The latest release of the Go language, Go 1.24, introduces several important features, including generic type aliases, weak pointers, improved cleanup finalizers, and more. It also enhances runtime performance in map
default implementation, small object allocation, and mutexes handling.
A type alias in Go provides a synonym for an existing type, which can be useful for readability and conciseness. Now, Go 1.24 allows creating type aliases for generic types, that is, a type alias can specify a type parameter.
type ComparableVector[T comparable] = Vector[T]
type ComparableVectorOfInts = ComparableVector[int]
type ThisWouldBeAnError = ComparableVector[[]int]
It’s worth recalling here that Go provides a similar syntax for defining a new type based on an existing type, e.g. type NewInt int
. Albeit the syntax only differs in the missing =
, the implications are great since NewInt
cannot be used in place of int
.
Interestingly, the discussion about whether introducing generic type aliases and their implications on the language has been going on for over three years.
Weak pointers do not increase the reference count of an object, so when an object is referenced only by weak pointers, the garbage collector can free it. As a consequence, you should check a weak pointer is not nil
before attempting to use its value:
var strongInt int = 5
var weakInt *int
weakInt = &strongInt
...
weakInt.Value()
Weak pointers may be useful when you want to implement, for example, an object cache to avoid objects being retained for the mere fact of being included in the cache.
Go finalizers serve the purpose of cleaning up things when an object is garbage collected. Prior to Go 1.24, this could be accomplished using [runtime.SetFinalizer](https://tip.golang.org/pkg/runtime#SetFinalizer)
, which has several caveats, including the impossibility of defining more than one finalizer on the same object, the fact that finalizers will not work on objects involved in a reference cycle, and so on. To overcome these limitations, Go 1.24 provides a new runtime function, AddCleanup
, which can be used to register a cleanup function with an object:
runtime.AddCleanup(objPointer, cleanupFunc, resourceToCleanUp)
...
func cleanupFunc(resourceToCleanUp CleanUpArgType) {
...
}
The cleanup mechanism fixes the issues with finalizers mentioned above. Additionally, it ensures all cleanup functions are called sequentially in a separate goroutine.
As mentioned, Go 1.24 improves the runtime performance of maps. In particular, it adopts SwissTable as a base for map
implementation and uses a concurrent hash-trie for the implementation of sync.Map
.
Using SwissTable brings 30% faster access and assignment of large maps, 35% faster assignment on pre-sized maps, and 10-60% faster iteration depending on the number and size of items in the map.
Similarly, adopting a concurrent hash-trie enables the new sync.Map
implementation to beat the old one on almost every benchmark.
Go 1.24 includes many more improvements and changes than what can be covered here, including new functions in bytes and strings packages, omitzero json tag, directory-limited filesystem access, etc. While the release notes are as usual quite terse you can find great video summaries on Reddit user GreenTowel3732’s YouTube channel.