It has been a while since a Go release included some actual language enhancements, but version 1.17 looks to change things up. The now available update to the Google-bred programming language also offers some security improvements, advancements in lazy loading, and help with deprecations.
Two of the language enhancements, unsafe.Add and unsafe.Slice, are meant to help developers write code that follows the rules for valid pointer use. While Add adds a len argument to a pointer and returns the updated pointer, Slice “returns a slice whose underlying array starts at ptr and whose length and capacity are len” for expression ptr of type *T, unsafe.Slice(ptr, len). Other than that Go now allows the conversion of a slice into an array pointer.
The Go team also implemented a new way of passing function arguments via registers which should speed up packages and programs when compared to the old approach that used the stack for that. Backwards compatibility with assembly functions is maintained through adapter functions the compiler generates. These convert calling conventions and shouldn’t be noticeable in most cases, however the team notes that a small overhead is to be expected when “calling an assembly function indirectly from Go via a func value, and calling Go functions from assembly”.
Starting with version 1.17, the module graph for minimal version selection only includes immediate dependencies of other go 1.17 modules instead of their full transitive dependencies, so effectively a pruned graph. To still be able to resolve transitive imports, go.mod files specifying go 1.17 or higher need to explicitly require every module that provides a transitively-imported package. Amongst other things, this change helps to realise lazy module loading, since it lets the go command only load the complete module graph when needed to complete a command. Old go.mod files can be upgraded via go mod tidy subcommands as mentioned in the documentation.
Module authors who would like to use the opportunity of the new release to deprecate a couple of old creations can now add a // Deprecated: comment immediately before or after the module directive in the go.mod file. Commands go list -m -u and go get have been altered to check for such information and will print warnings along with any instruction provided by the author.
Go core library Cgo has learned to turn any Go values into a safe representation, in order to make passing values between C and Go more secure. Another potential security issue is meant to be avoided by a change in URL query parsing: packages net/url and net/http now no longer accept a semicolon as setting separator in URL queries, and log warnings should they be used nonetheless. The old behaviour can be restored for net/http by using handler wrapper AllowQuerySemicolons, but it isn’t recommended. Grepping for and replacing semicolons with ampersands would be a more secure option to make sure everything keeps working as expected.
To mitigate the ALPACA cross-protocol attack which was publicly disclosed in June, servers now enforce an overlap between configured protocols and the ALPN protocols the client advertises if Config.NestProtos is set. If the parties don’t have any protocol in common, the connection is closed with an appropriate alert (except for when HTTP/1.1 clients try to connect to a server whose config includes the “h2” value).
Other interesting library changes include a new Values.Has method for testing whether a query parameter is set in the net/url library, a rewrite of the crypto/ed25519 package that should speed it up on amd64 and arm64, and method to let the user cancel an in-progress TLS handshake. Speaking of TLS, starting with Go 1.18, crypto/tls clients will default to version 1.2 of the protocol for better security, though applications will still be able to explicitly set a different Config.MinVersion to allow a wider array of connections. In terms of ports, Go 1.17 now supports 64-bit ARM architectures on Windows and the openbsd/mips64 port has been adjusted to work with cgo.
Developers surprised by the small amount of language changes landing in releases lately need to consider the Go project’s evaluation process. The latter was introduced by the Go team in 2018 to make sure language modifications help significantly more developers than they would hurt, in order to not risk a split in the rather large Go ecosystem.
Since then, the team selects a small number of proposals that fit the bill of addressing a widely important issue with minimal impact on everybody not interested for each release, discusses them with the community, and starts implementing from there. Currently, the Go project’s repository clocks a full 120 language proposals. Getting through the discussion stage isn’t exactly easy though, the try proposal for better error handling for example was stopped in its tracks a good while before the evaluation period ended.