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.
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.
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
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.
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.
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:
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
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.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.
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
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.