Go 1.21 has been released!

August 10, 2023

I’m a couple days late with the news… so you hopefully didn’t hear it here first. But two days ago, Go 1.21 was released.

This means the Go spec has changed! Today I’ll highlight the main changes introduced to the spec in Go 1.21. None of the changes substantially impacts topics we’ve already covered in the spec series, so I won’t be going back to re-explain changed parts of the spec we’ve already covered. And I’ll save an in-depth discussion of each of these new areas until we come to them naturally in our tour through the spec.

From the release announcement we see that there have been a number of interesting changes, but three items stand out as language changes. These are the parts that alter the spec:

Language changes

  • New built-in functions: min, max and clear.
  • Several improvements to type inference for generic functions. The description of type inference in the spec has been expanded and clarified.
  • In a future version of Go we’re planning to address one of the most common gotchas of Go programming: loop variable capture. Go 1.21 comes with a preview of this feature that you can enable in your code using an environment variable. See the LoopvarExperiment wiki page for more details.

Actually, we’re only going to address the first to points for now, as the third point is still only experimental, and not an officially adopted language change.

min and max should need no explanation, at least as far as what they generally do. These types of functions are common in most languages. And they mostly do what you might expect.

The built-in functions min and max compute the smallest—or largest, respectively—value of a fixed number of arguments of ordered types.

m := min(x)                 // m == x
m := min(x, y)              // m is the smaller of x and y
m := max(x, y, 10)          // m is the larger of x and y but at least 10

I think you get the idea.

clear is a bit more special. When you call it on a map it does exactly what you would probably expect: It deletes all elements from the map.

However, when you call clear on a slice, it doesn’t result in a zero-length slice, rather it sets all of the elements of the slice to their zero value. We’ll talk more about this in due course.

And finally, the change to type inference. This is a bit difficult to explain without context, so I’m going to save this one until we’re at that point in the spec. It should make more sense then.

For now, go upgrade your Go installation, if you haven’t already, and enjoy some of the performance improvements and other new standard library features!

Share this

Related Content


There are a number of terms that get thrown around, often semi-interchangeably by the less initiated (such as myself). “Declaration”, “definition”, “statement”, … and today’s topic “expressions”, just to name a few. But, at least within the context of the Go spec, most such terms have very specific meanings. Expressions An expression specifies the computation of a value by applying operators and functions to operands. So: type foo int and var foo int are not an expressions.

Underlying types

We’ve made it through the complete list of types in Go. Now we’re going to dig into some of the fundamentals, with a tour of general “Properties of types and values”. Properties of types and values Underlying types Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T’s underlying type is the underlying type of the type to which T refers in its declaration.

Restrictions on underlying type terms

General interfaces … In a term of the form ~T, the underlying type of T must be itself, and T cannot be an interface. type MyInt int interface { ~[]byte // the underlying type of []byte is itself ~MyInt // illegal: the underlying type of MyInt is not MyInt ~error // illegal: error is an interface } Once again, the spec examples are pretty well explained. TL;DR; the ~ prefix must always be associated with an underlying data type.