rss twitter gitlab github linkedin linkedin instagram
What is new in Go 1.17?
Aug 27, 2021

Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I’ll earn a commission. Please notice your final price is not affected at all by using those links.

There’s a new minor version of Go, superseeding Go 1.16, which was released about a week ago on August 16, 2021, this is Go 1.17! 💥 🎉 🎊

What is new?

There are multitude improvements included in this release, for this post specifically I will be focusing on those changes that may need to be considered because they could break your current services if you currently use those features, and I will also share some of the new APIs that were also added that should make our life easier.



Updating

Depending on your development platform and the production system you use for running your service you may already have access to start using Go 1.17, however as usual downloading Go is available via the official page.

For more concrete examples:

Running binaries using versions

The code used for this post is available on Github.

Similar to what was added in 1.16 when installing binaries started to support versions:

go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.41.1

We now can do something similar for the run command:

go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.41.1

The commands look very similar however the biggest difference is that if we use run the dependencies in our go.mod won’t be updated to reflect the dependencies the tool we are running is using, this way we can run tools without affecting our local dependencies therefore reducing our overall dependencies.

go vet os/signal warnings

The code used for this post is available on Github.

This change is better explained with the following example using the signal package:

func main() {
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt)

	fmt.Println("waiting")

	<-c

	fmt.Println("received")
}

If we run vet against that code we should be getting a warning similar to:

# command-line-arguments
./main.go:11:2: misuse of unbuffered os.Signal channel as argument to signal.Notify

This is because the channel we are using for receiving the os.Signal messages (c) is unbuffered, if we change it to become buffered the warning will go away.

go vet error warnings

The code used for this post is available on Github.

This is related to the changes added in Go 1.13 to the errors package, specifically when error type implement those new APIs (Is, Unwrap and As). For example our error type CustomError:

type CustomError int

func (_ CustomError) Error() string              { return "error message" }
func (_ CustomError) Is(err error) bool          { return false }
func (_ CustomError) As(target interface{}) bool { return false }
func (_ CustomError) Unwrap() error              { return nil }

Is implementing all three methods correctly, but if we break the expected API:

type CustomError int

func (_ CustomError) Error() string                     { return "error message" }
func (_ CustomError) Is(b, err error) bool              { return false }
func (_ CustomError) As(_ int, target interface{}) bool { return false }
func (_ CustomError) Unwrap(_ int) error                { return nil }

We will get some warnings:

# command-line-arguments
./main.go:11:22: method Is(b error, err error) bool should have signature Is(error) bool
./main.go:12:22: method As(_ int, target interface{}) bool should have signature As(interface{}) bool
./main.go:13:22: method Unwrap(_ int) error should have signature Unwrap() error

URL Query Args Parsing

The code used for this post is available on Github.

This is not common but it’s something to keep in mind when using the url.Parse function because the results will be different depending on what Go version we use.

For example:

func main() {
	u, err := url.Parse("https://url.xyz/?arg1=one;arg2=two&arg3=three")

	fmt.Println("err", err)
	fmt.Printf("%+v\n", u.Query())
}

Will print out different values depending on whether the Go version is 1.16 or 1.17, for 1.16 the output will be:

err <nil>
map[arg1:[one] arg2:[two] arg3:[three]]

And for 1.17 the output will be:

err <nil>
map[arg3]:[three]]

This is because now & is the delimiter to use for determining the query arguments; if you still need to keep the previous behavior in your handler then you should use net/http.AllowQuerySemicolons.

Daylight Savings Time added to time.Time

The code used for this post is available on Github.

This is better explained with:

func main() {
	fmt.Println("DST?", time.Now().IsDST())
}

It’s a new method added to the time.Time type that indicates whether the current time is DST, small change but really useful in cases you deal with different ways to display time depending on the timezone.

go test -shuffle

The code used for this post is available on Github.

This is a sort of a quick one to add to your current tests suite, basically adding -shuffle=on should do it:

go test -shuffle=on -race -coverprofile=coverage.txt -covermode=atomic ./...

Conclusion

Those are the new features I found really interesting, however I know for sure in 1.18 new things will be added like Fuzzing and Generics! Looking forward to Go 1.18!

If you’re looking to sink your teeth into more Go-related topics I recommend the following books:


Back to posts