Update: Want to listen to a podcast about Go 1.19 instead of reading a blog post? Check out Go Time episode 240.

Go 1.18 was a big release with huge features like generics, fuzzing, and workspaces. There was plenty of excitement around its minor or even ultra-minor features. Go 1.19 is not a release on that scale. But it does pack in a lot of small improvements that can help the average Go developer. Let’s take a look at what some of them are.

Stuff I worked on

It’s my blog, so of course I’m going to start with the features that I helped work on. 😊 I can’t actually take full credit for any of the features I helped get into 1.19, but I did get my name listed for the commits in git, so it counts.

New url.JoinPath

url.JoinPath is a new function and method pair in the url package that does what it says on the tin. A user called wanglong001 on Github suggested the feature, and we batted around the implementation details in the issues. I was particularly interested in it because I had just worked on a URL path joining feature for my HTTP requests library.

New time.Duration.Abs()

This feature was born out of a bug that bit me in production. 😊 If you have two time.Times and want to know if they’re within N minutes of each other, you might try to subtract them, convert the difference to a positive number if it’s negative, then see if that duration is less than N minutes. That doesn’t work because time.Duration can represent one more nanosecond in negative numbers than in positive numbers, since it’s just an ordinary signed int64 under the covers. This will come up anytime one of the two time.Times is zero because the zero time is longer ago than can fit into a time.Duration.

In Go 1.19, the new method time.Duration.Abs() fixes the problem by just rounding down in the case of saturation.

Russ Cox, as always, was extremely helpful in pointing me in the direction of a better solution after I opened the issue.

New http.MaxByteError

The underlying issue for this dated back to 2019. As with http.MaxBytesHandler, what pushed me to work on this was reading the great Let’s Go Further by Alex Edwards. The problem is that if you use a http.MaxBytesReader and want to return a proper error message to your faulty clients, there wasn’t a reliable way to detect the errors from http.MaxBytesReader until now. You had to just look at the error string and see if it was exactly "http: request body too large". If the Go team ever changed this error string, the check would break.

The new http.MaxBytesError lets you use errors.As to check for the error reliably and send a proper response. It also lets you see what the limit was that the client exceeded.

Stuff I’m just a fan of

First use of generics in the standard library

We’ve all been waiting for years for Go to get generics, but the Go team have deliberately chosen to keep the roll out of generics to the standard library slow for now.

Well, blink and you’ll miss it, but here is the first new API in the standard library to use generics: atomic.Pointer[T].

In prior versions of Go, you could use atomic.StorePointer and friends to achieve this same functionality, so it’s not really a new capability, but it is a much more convenient way to use the feature.

These new features flowed out of Russ Cox’s work on revising Go’s memory model. As before, the memory model warns, “If you must read the rest of this document to understand the behavior of your program, you are being too clever.” For most programmers, the long and short of the changes are that Go is now documenting that its memory model guarantees are more or less similar to other languages, but nothing should change in code that the average programmer writes.

Go doc improvements

One of the nice features of Go has always been how the parts of the go tool work together: it formats code with go fmt, runs tests with go test, and provides documentation with go doc. However, the exact formatting that go doc expects was a bit cryptic. It supports headings, lists, and blockquotes, but you had to know to read the right Github repo to figure out how to do it.

Fortunately, for Go 1.19, the formatting expected by go doc has been clarified and now go fmt will even clean up certain formatting issues with doc comments. You can also include links in your doc comments, and it should display properly on pkg.go.dev.

Soft memory limit

Go deliberately restricted the number of “knobs” that could be used to tune garbage collection to try to keep things working simply for the large majority of users. Setting GOGC lets you tell Go when to trigger the next round of garbage collection based on the percentage of new memory allocated versus old memory remaining. This lead to strange tricks like Twitch’s “memory ballast” optimization, in which they tried to smooth out garbage collection by just allocating a big slab of memory that they didn’t actually need.

Go 1.19 adds a soft memory limit that can be controlled by the environment variable GOMEMLIMIT or runtime/debug.SetMemoryLimit. Michael Knyszek wrote the proposal for this issue, which should make “memory ballast” obsolete and make it easier to use Go in devices like phones and tablets with a lot of RAM but a hard ceiling. It essentially sets a target and triggers increasingly aggressive garbage collection (within a CPU limit) as the amount of memory used gets closer to that target.

More AppendX functions

Go 1.18 added bufio.Writer.AvailableBuffer() to make the low level append-to-preallocated-byte-slice pattern a little easier to use.

Go 1.19 expands this pattern greatly by adding fmt.Append, fmt.Appendf, and fmt.Appendln. These functions work like fmt.Print etc. but instead of printing to standard out or an io.Writer or a string, they append to a byte slice, which allows for very precise control of memory allocation.

New flag.TextVar

I am a big fan of the flag package, so I’m always happy to see improvements to it. The flag.TextVar function and method let you use any type which implements encoding.TextUnmarshaler directly as a flag without needing to write an adapter. Some example types that already implement TextUnmarshaler are big.Int, net.IP, and time.Time.

New sort.Find and new sort algorithm

While we’re waiting for slices.Sort to be added to the standard library (with its promised speed up), the good old sort package is making some improvements of its own. Rob Pike proposed sort.Find, which is a new way to search for an item in a presorted sequence.

Also, the algorithm for sort.Sort has been slightly tweaked to use the pattern defeating quicksort variation. It is still O(n log n) on average and in the worst case, but it has a best case time of O(n) that it can achieve in some cases, such as when the sequence is already sorted.

Wrapping up, Go 1.19 won’t be remembered as the kind of paradigm shattering release that 1.18 was, but it brings a steady accumulation of small improvements in the lives of Gophers. You can download the release candidate now and the final version should be released soon. Enjoy and see if you can find some issues to contribute to for Go 1.20 and beyond.