go: spec: `any` no longer implements `comparable`
What version of Go are you using (go version
)?
$ go version go version go1.18rc1 darwin/arm64
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ go env GO111MODULE="" GOARCH="arm64" GOBIN="" GOCACHE="/Users/jb/Library/Caches/go-build" GOENV="/Users/jb/Library/Application Support/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="arm64" GOHOSTOS="darwin" GOINSECURE="" GOMODCACHE="/Users/jb/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="darwin" GOPATH="/Users/jb/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/opt/homebrew/Cellar/go/1.18rc1/libexec" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/opt/homebrew/Cellar/go/1.18rc1/libexec/pkg/tool/darwin_arm64" GOVCS="" GOVERSION="go1.18rc1" GCCGO="gccgo" AR="ar" CC="clang" CXX="clang++" CGO_ENABLED="1" GOMOD="/Users/jb/code/golang-set/go.mod" GOWORK="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/qv/ll7pql3n7dbf5w8rf46bcttm0000gn/T/go-build4282451061=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
https://go.dev/play/p/jML-W5X8dks?v=gotip
What did you expect to see?
Program exited.
(no error)
What did you see instead?
./prog.go:16:17: any does not implement comparable
Go build failed.
Alternatively, using comparable
rather than any
, this error:
./prog.go:16:20: interface is (or embeds) comparable
Go build failed.
Ref: https://github.com/golang/go/issues/50646#issuecomment-1044754308
I understand that this seems to be intended, but I think it’s going to make it practically quite difficult for developers to use generics in Go - as far as I can tell, there is simply no way to make this work
I think this should be reconsidered, or at least discussed more widely - as it does not seem to be specified in the proposal
This seems significant enough to be considered as a release blocker
A library which is affected in practice: https://github.com/deckarep/golang-set/issues/79 (using its generics
branch) - I have been trying to find workarounds to allow for this new behaviour, but have not managed to resolve it
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 3
- Comments: 16 (12 by maintainers)
I think this issue should have gone through the proposal process. The type parameters design doc explicitly states:
The definition of “comparable” at the time that proposal was approved states (emphasis mine):
By those definitions, every interface type should be included in the type-set of
comparable
, and thusany
— as an interface type itself — should be in that type-set.Redefining
comparable
not to include interface types is a very late and drastic departure from the design that was approved in #43651.I believe this is just a bug: When we check if a type implements
comparable
, we must always use the same criteria for comparability. In this case:the type
T
is not an interface and the code is using the traditional (before 1.18) predicate to determine ifT
is comparable. It should be using an updated version.I think this needs to be fixed for 1.18 otherwise we’ll have to maintain this inconsistency.
I agree that the long-term solution is to allow
comparable
as an ordinary type. See also my comment here:@billinghamj Note that
Set
is declared as follows:A
Set
is a map, and maps are not comparable, they have never been. So you can’t use aSet
(i.e., a map) as a key for another map. This is also not possible in non-generic Go.FWIW I am not sure that
[]comparable
really fixes the inconsistency. For example this works today:It is odd that
[]T
(where T contains any) is allowed but[]any
is not. It feels like an inconsistent cut-out. But I completely agree about waiting until after Go 1.18 to figure this out.Is there any reason to believe we can’t relax this in a future release? Being conservative for now seems like the best answer.
Notably, if interfaces do not satisfy
comparable
, then a transition like this for sync.Map doesn’t work: