Go 1.19 delivers small but very interesting updates to the language, the standard library, and the toolchain. Here are some of the highlights.
This is my favorite update in Go 1.19. Doc comments are invaluable for package documentation. They allow generating package documentation right from the source code, following the Single Source of Truth principle. Now they have become even better.
Doc comments now support formatted external links as well as cross-reference links to other package documentation.
For external links, Go uses a syntax similar to Markdown's shortcut reference link format that keeps the source text readable:
// NewsFilter uses a [bloom filter] and threfore
// may return [false positive] matches.
//
// [bloom filter]: https://en.wikipedia.org/wiki/Bloom_filter
// [false positive]: https://en.wikipedia.org/wiki/Type_I_and_type_II_errors
Cross-references to other package documentation come in two flavors:
[Name]
or [Name1.Name2]
.[pkg]
, [pkg.Name1]
, or [pkg.Name1.Name2]
.// NewsFilter uses [crypto.Rand] to generate matches if the actual matches exceed the boringness threshold.
Simple, un-nested lists can be created either as a bullet list or a numbered list.
In either case, the list must be indented to be identified as such. A leading blank line is not necessary.
A bullet list item starts with either *
, +
, -
, or a Unicode bullet followed by a space or tab:
// NewsFilter can run in one of three modes:
// - linear covalence
// - double-flex recursive attractor grid
// - Infinite impossibility
//
// Note:
Numbered lists start with a decimal number followed by a period (1.
) or right parenthesis (2)
):
// NewsFilter gets unrecoverably instable in these cases:
// 1. Non-nil input
// 2. Negative thoughts
// 3. Fake news
A heading is now clearly defined by a leading hash/number sign (#
) followed by a space, similar to a Markdown level-1 heading. The heading line must be a single line and not be indented, and a blank line must separate a heading from the subsequent paragraph.
// # NewsFilter caveats
//
// These caveats apply when running NewsFilter on any news not nodded through by ACME Corporation.
Read more about the new doc comment features in the Go Doc Comment documentation.
Related to the doc comment updates, a new package go/doc/comments
allows parsing doc comments and rendering them as a doc comment, HTML, Markdown, or plain text with a few lines of code:
var p comment.Parser
doc := p.Parse(text) // text = a doc comment without comment markers
var pr comment.Printer
os.Stdout.Write(pr.Text(doc)
See comment package - go/doc/comment - Go Packages for details.
The Go runtime now allows to set a soft memory limit that affects all memory managed by the runtime itself. (External memory sources like memory held by the OS on behalf of the Go process are not affected.)
The memory limit can be set by either calling runtime/debug.SetMemoryLimit()
or by setting the environment variable GOMEMLIMIT
.
The memory limit sets a maximum on the total amount of memory that the Go runtime can use. The garbage collector uses this limit to set the total heap size, and it may then run more often to keep memory usage below the limit.
The increase of GC cycles may lead to thrashing when the total memory gets close to the memory limit. For this reason the limit is only a soft limit, with no guarantees that the runtime maintains this limit under all circumstances.
Read more in the Go GC Guide.
(...where "staff" = me.)
The changes to the standard library are numerous, so let me pick a few that I find particularly interesting.
The type Value
in package sync/atomic
now has typed cousins: Bool
, Int32
, Int64
, Pointer
, Uint32
, Uint64
, and Uintptr
. These new types help improving the correctness of a program.
Why is there not just a generic atomic.Atomic[T]
instead of the new set of types? Russ Cox answers this in his blog post about the new memory model:
I don't see a clean way to use generics to provide just a single
atomic.Atomic[T]
that would let us avoid introducingBool
,Int
, and so on as separate types, at least not without special cases in the compiler. And that's okay.
Go has the Go 1 Backward Compatibility Promise. Under this promise, new Go compiler releases shall not break any existing code. That is, code that was written and tested with Go 1.0 should still compile fine with Go 1.19.
However, there are exceptions. Especially, important security fixes may introduce breaking changes to Go. PATH lookups are such an exception.
In a nutshell, exec.LookPath()
no longer searches for a command relative to the current directory. Instead, it returns that path along with an error.
That is,
path, err := exec.LookPath("ls")
will not return an "ls" command that it finds in the current directory or in subdirectories of it. This behavior prevents running a malicious copy of "ls" inadvertently.
Find the details in Command PATH security in Go - The Go Programming Language, but for this article, it is sufficient to know that this change may break code that calls, for example, exec.Command("newsfilter")
to run the command newsfilter
(or newsfilter.exe
on Windows) that is located in the current directory. (Side note: exec.Command()
calls exec.LookPath
behind the scenes.)
If you do have such calls in your code, rewrite them as exec.Command("./newsfilter")
(or ".\newfilter.exe
" on Windows).
A nice addition has arrived for the flag
package. The new function TextVar()
allows specifying flags for custom types. For example, you can define a flag that is parsed into a custom struct
type. Or a flag for time.Time
or netip.Addr
types.
To accomplish this, you only need to implement enconding.TextUnmarshaler
accordingly.
Instead of diving into the details here, I suggest reading this blog post instead, where the author lays out a nice and short example:
Define custom command-line flag types in Go 1.19 | by Cheikh seck | Aug, 2022 | Dev Genius
The fmt
package has new functions Append
, Appendf
, and Appendln
that append arbitrarily typed data (any
data, that is) to byte slices.
Example:
s := []byte{}
s = fmt.Append(s, 42)
s = fmt.Appendf(s, "-> %08d", 42)
fmt.Println(string(s))
Output:
42-> 00000042
Running Go code with the -race
flag makes the executable considerably slower. With Go 1.19, the situation has improved: The new thread sanitizer v3 is typically 1.5x to 2x faster than v2. Moreover, it uses only half as much memory and supports an unlimited number of goroutines.
The Go 1.19 update is by far not as massive as Go 1.18 was. Nevertheless, Go 1.19 has some useful updates worth memorizing—your next project may benefit from one of them.
See the full list of changes in the Go 1.19 Release Notes - The Go Programming Language.
Happy coding!
Image based on a photo by Marten Bjork on Unsplash.
Categories: : Releases, The Language