golang 1.8

With the release of Go 1.18, many Gopher’s can’t wait to download the release and experience the new features!

After Go 1.18 arrives, what is the first thing you want to do? Speaking of which, many people will ask: What meme is this?

This meme comes from Russ Cox on December 1, 2021 a commit to the Go language project.

a commit to the Go language project

As you can see from the commit log, the main purpose of this change is to replace all interfaces{} in the code of the Go project src directory with any. Anyone who has studied Go knows that interface{} is called an “empty interface” in Go, and all types implement the empty interface interface{}. An instance of any type T can be assigned to an empty interface type variable.

1
2
3
var t T // T可以是任意Go类型
var i interface{} = t
var j interface{} = &t

So why did the Go team replace all interfaces{} with any before the release of Go 1.18 beta1, and what is any? After looking through the Go 1.18 beta1 code, we found the definition of the any type in builtin/builtin.go.

1
2
3
4
// $GOROOT/src/builtin/builtin.go

// any is an alias for interface{} and is equivalent to interface{} in all ways.
type any = interface{}

We see that any is a type alias of interface{}, which is exactly equivalent to interface{} . So why add any and replace interface{}? I think it’s mainly because of the impact of Go 1.18’s inclusion of generics. Look at the following two function declarations that use generic syntax.

1
2
func f[T1 any, T2 comparable, T3 any](t1 T1, t2 T2) T3 { }
func f[T1 interface{}, T2 comparable, T3 interface{}](t1 T1, t2 T2) T3 { }

With the addition of the type parameter to the Go generic, if we continue to use interface{} in the type parameter declaration area, we see that the function declaration section will be very long and less comfortable for the developer’s senses. In addition, after Go 1.18 introduced generics, the interface type took on another responsibility: defining constraints for type parameters, and using a name like any was a better match for the new responsibility. So Go 1.18 introduced the type alias for interface{} and did a global replacement.

Of course there are those who love such things, and those who oppose them.

gopher oppose

However, we have seen that most gopher’s still prefer any to the “length” of interface{}.

Since the Go language project itself has done this, we as Gopher’s are obliged to answer the call to switch to Go 1.18 and start replacing all the interface{} in our code with any. It’s easy! gofmt does it all! The following are the specific steps.

  • Check the usage of interface{} under the current project
1
2
3
4
$find . -name "*.go"|xargs grep "interface{}"

// 如要排除掉vendor
$find . -name "*.go"|grep -v vendor|xargs grep "interface{}"
  • View the list of source files that will be affected by this replacement
1
2
3
4
$gofmt -l -r 'interface{} -> any' .

// 如要排除掉vendor
$gofmt -l -r 'interface{} -> any' .|grep -v vendor
  • Execute global replacement
1
2
3
4
$gofmt -w -r 'interface{} -> any' .

// 如要排除掉vendor目录
$find . -name "*.go"|grep -v vendor|xargs gofmt -w -r 'interface{} -> any'

Note: gofmt does not replace the interface{} in the comment

Finally, the replacement can be checked with the following command.

1
$find . -name "*.go"|xargs grep "interface{}"

After a while …..

You might think you’re being a little “impulsive “! Although Go 1.18 supports any, Go 1.17 and previous versions do not. Unless the team is in lockstep to upgrade to go 1.18, the rest of the team may not be able to compile the code you submitted by replacing interface{} with any! What to do?

Considering compatibility with Go 1.18 up to Go 1.9, we can use conditional compilation to solve this problem. Look at the following example.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// https://github.com/bigwhite/experiments/tree/master/emptyinterface2any 

$tree emptyinterface2any
emptyinterface2any
├── any.go
├── demo
├── go.mod
├── main.go
├── pkg1
   ├── any.go
   └── pkg1.go
└── pkg2
    ├── any.go
    └── pkg2.go

In this emptyinterface2any demo project, all interfaces{} are replaced with any. we build this demo with go 1.18 without any problem. However, if we use Go 1.17 or earlier, we will get the “any is undefined” error. To be compatible with older versions, we add an any.go file to the bottom of each package.

1
2
3
4
5
6
7
8
// https://github.com/bigwhite/experiments/tree/master/emptyinterface2any/any.go

// +build !go1.18
//go:build !go1.18

package main

type any = interface{}

We see that in this file we have added compile constraint indicator information, by which we tell the compiler that this source file only participates in compilation when built with versions prior to Go 1.18, and that when Go 1.18 is compiled, it does not participate in compile. This way when compiling with Go 1.17 and earlier, the file participates in compilation, which is equivalent to us customizing an any alias type.

This works for Go versions in the Go 1.9, Go 1.17 range, since the type alias syntax was introduced in Go 1.9.

Now you can submit your code without any worries, although adding any.go and compiling with constraints is a bit of a hassle ^_^. However, this is temporary, and once you have migrated to Go 1.18 and later, these temporary measures can be removed (remove all any.go).