Go 1.24 is set to be released in February 2025, and it is bringing some amazing features that I couldnβt wait to get my hands on. In this post, Iβll cover Generic Type Aliases and the new tool directive. This post kicks off the first in a series of Go 1.24 editions, where Iβll explore the new features in detail.

Generic Type Aliases
Generics were introduced in Go 1.18, making it simpler to work with different types without sacrificing type safety. Aliases were introduced in Go 1.19, but without support for generics. Now, Go 1.24 is bringing the complete package with Generic Type Aliases.
Let me explain the difference between a type definition and a type alias before getting into generic type aliases.
Type Definition
Type definitions are useful when you want to create a completely distinct type based on an existing one. With this, we can add custom methods and behaviors to the new types and ensure weβre not accidentally mixing them.
type Celsius float64
type Fahrenheit float64Here, Celsius and Fahrenheit are distinct types, even though they both share the underlying type float64. We can define different methods for each type, preventing unintended operations between them.
It improves type safety by preventing us from accidentally passing Celsius to a function expecting Fahrenheit. It also makes the code more readable.
Itβs worth mentioning when you create a type definition, it gets the underlying data type's fields but not its methods. For example, in the following code, Manager has access to age and name fields from Employee, but it doesnβt get the Title method.
type Employee struct {
name string
age int
}
func (e Employee) Title() string {
return "Employee"
}
type Manager Employee
func (m Manager) DescribeRole() {
title := m.Title() // Does NOT compile
fmt.Printf("%s is %d years old and they are a(n) %s!\n", m.name, m.age, title)
}Type Alias
Type aliases provide an alternative name for an existing type without creating a new, distinct type.
type Manager = Employee
func (m Manager) DescribeRole() {
title := m.Title() // It compiles
fmt.Printf("%s is %d years old and they are a(n) %s!\n", m.name, m.age, title)
}Here, Manager is simply another name for Employee. The compiler treats Manager and Employee as interchangeable. Type aliases are handy for code refactoring and improving readability, especially when dealing with long or complex type names.
Until now, we couldnβt use type aliases with generic types, but Go 1.24 introduces this feature. Letβs look at an example to make sense of it.
Suppose we have a generic type Pair in a package called oldpkg:
package oldpkg
type Pair[T any] struct {
First T
Second T
}Assume we want to deprecate oldpkg and move Pair to a new package newpkg while maintaining full compatibility. Type aliases become incredibly useful when moving types and guiding users toward the new package.
Before Go 1.24, you couldnβt directly create a type alias for Pair[T any] because type aliases didnβt support type parameters. But now, we can define the following alias:
package newpkg
import "path/to/oldpkg"
type Pair[T any] = oldpkg.Pair[T]With this alias, newpkg.Pair and oldpkg.Pair are treated as the same type, ensuring complete compatibility. The [T any] part ensures that the alias also captures the generic type parameter.
New βtoolβ directive
Go 1.24 introduces a new tool directive in go.mod to track executable dependencies. Letβs break down what problem it solves and why itβs so useful.
If youβve looked at the Kubernetes codebase, you might have seen files called tools.go. Hereβs an example of one. These files import tools using _ and donβt actually do anything with them:
// Package tools is used to track binary dependencies with go modules
// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module
package tools
import (
// linting tools
_ "github.com/aojea/sloppy-netparser"
_ "github.com/client9/misspell/cmd/misspell"
// ...
// Some more imports
// for publishing bot
_ "golang.org/x/mod/modfile"
_ "k8s.io/publishing-bot/cmd/publishing-bot/config"
)When working on a project, we often need tools for tasks like linting, testing, or code generationβtools like stringer, protoc, or golangci-lint.
Managing these tools in a team can be tricky because people might have different versions installed. This can cause inconsistent outcomes and unexpected issues.
So, why not just add them to go.mod? Well, as these tools arenβt directly used in your code, go mod doesnβt recognise them as dependencies. When you run go mod tidy, it removes them.
The Old Workaround: tools.go Pattern
To deal with this, the hack was using a file called tools.go. This file imports all tool dependencies using _ so that go.mod treats them as dependencies.
install-tools: download
@echo Installing tools from tools.go
# Not a proper one as it doesn't install specific versions
@cat tools.go | grep _ | awk -F'"' '{print $$2}' | xargs -tI % go install %
The Go 1.24 Solution: tool Directive
Go 1.24 introduces the tool directive, which removes the need for the tools.go hack. Now, you can directly define your tool dependencies in go.mod .
Letβs say I want to install go.uber.org/mock/mockgen@latest to generate mock implementation of some interfaces.
π 1. Add and Manage Tool Dependencies
We can add mockgen tool dependency to our module using:
go1.24rc1 get -tool go.uber.org/mock/mockgen@latest
π οΈ 2. Run Tools Easily
Once a tool dependency is added, we can run it directly using the go tool command:
go1.24rc1 tool mockgenπ 3. List Available Tools
To see all tools currently available in our module, run: go tool
(base) β go-124 go1.24rc1 tool
addr2line
asm
buildid
cgo
compile
covdata
cover
doc
fix
link
nm
objdump
pack
pprof
preprofile
test2json
trace
vet
go.uber.org/mock/mockgenπ 4. Update All Tools
You can update all tools in go.mod with:
go1.24rc1 get -u toolπ¦ 5. Install All Tools
Install all tools to $GOBIN directory using:
go install toolThis is just the tip of the iceberg when it comes to Go 1.24. Thereβs a lot more to explore, including a new map implementation using Swiss Table, the mlkem package for post-quantum cryptography, and the synctest package for testing concurrent code. Iβll be covering the rest in the upcoming posts.
If you donβt want to miss them, make sure to subscribe and get them straight to your inbox!


