~ 4 min read

Go 1.21: Now with More Gopher Power and Less 'Go-tchas'!

Explore innovative features, including enhanced map and slice operations, structured logging...
Image credit: www.midjourney.com

The Exciting New Features of Go 1.21

Welcome to August 2023, and guess what’s hot? Go 1.21 is here, and it’s all set to make coding easier and smoother for all us Gophers out there. Packed with cool new stuff and tweaks, it’s all about boosting speed, making things work better, and giving us an easier time as developers.

So, let’s not waste any more time. Let’s jump in and see what Go 1.21 has got in store for us!

Clear Built-in Function

Prior to Go 1.21, to clear a map or a slice, we had to manually iterate over the elements and remove or zero them out. Now, the new clear built-in function takes care of this for us:

m := map[string]string{"foo": "bar", "hello": "world", "gopher": "go"}
clear(m) // m={}

s := []string{"foo", "bar"}
clear(s) // s=["", ""]

This significantly simplifies the code and even handles edge cases, like NaN, that previous versions couldn’t.

Loop Variable Experiment

A new opt-in experiment that changes the semantics of loop variables to prevent unintended sharing in per-iteration closures and goroutines. With the GOEXPERIMENT=loopvar flag, the compiler ensures that each iteration creates a new variable, thus eliminating a common bug in many codebases.

// Use this when you have to run a large number of goroutines
// and you don't want them to accidentally share the loop variable.
$ GOEXPERIMENT=loopvar go1.21rc3 run main.go

Structured Logging with slog

The new log/slog package, introduced in Go 1.21, provides structured logging with levels, emitting key=value pairs for machine processing. It’s flexible, customizable, and even allows you to rewrite log keys and values on the fly. Here is an example of its usage:

logger := slog.New(slog.NewTextHandler(os.Stderr, nil))

// Use it like this:

logger.Info("hello", "counter", 3)

It’s expected that this package will become the standard way to handle logging in Go, increasing library portability.

Maps and Slices Packages

Go 1.21 introduces two new packages to the standard library: maps and slices. These packages provide functions for very common maps and slice operations. Here are a few examples of how to use these new functions:

test := []string{"A", "C", "B", "A"}

// Use the new slice functions

slices.IsSorted(test) // false
slices.Sort(test) // [A A B C]
slices.Compare(test, []string{"A", "A", "B", "C"}) // 0
slices.BinarySearch(test, "B") // 1 true
slices.Reverse(test) // [C B A A]
slices.Insert(test, 1, "D") // [A D A B C]
slices.Compact(test) // [A C B A]

OnceValue and OnceFunc

With the new sync.OnceValue and sync.OnceFunc, you can delay expensive computations until they’re really needed. This saves resources and can lead to significant performance improvements in your code.

onlyOnce := sync.OnceValue(func() int {
    // Expensive code here
    return 42 // Return your computed int

firstCall := onlyOnce() // Invokes the function and returns the computed int
secondCall := onlyOnce() // Returns the computed int without invoking the function again

Built-in Min and Max Functions

With the new built-in min and max functions, you can now easily find the minimum and maximum values from a list of arguments without having to write your own comparison functions. This saves you time and keeps your code clean and efficient.

m := min(x, y)    // m is the smaller of x and y

c := max(1, 2.0)  // c == 2.0

t := max("", "foo", "bar") // t == "foo"

Profile Guided Optimization (PGO)

Go 1.21 significantly simplifies the way you enable PGO optimization. Now, a pprof CPU profile named “default.pgo” is stored in the main package directory of the profiled binary by default. This makes the profile readily accessible to the ‘go build’ command, triggering PGO optimizations seamlessly during the build process.

# Place default.pgo profile in the main package directory
$ go build
# If default.pgo is present, the build process will automatically apply PGO optimizations

Conclusion: Wrap up Go 1.21

This version brings an assortment of significant enhancements to make our coding lives easier and more efficient. From the efficient handling of maps and slices to structured logging and loop variable experimentation, Go continues to evolve in response to the needs of its developers. The introduction of Profile Guided Optimization hints at the commitment to better performance, reinforcing Go’s status as a high-performing language.

As we continue to work with Go 1.21 and beyond, let’s take advantage of these exciting new features and improvements. The Go community remains a source of innovation and robust debate - always seeking to balance simplicity and maintainability with performance. As we have seen with the changes in this release, the future of Go is exciting. Keep on Go-ing, gophers!