go icon indicating copy to clipboard operation
go copied to clipboard

reflect: Value.Comparable returns false for nil interface value

Open donwitobanana opened this issue 1 year ago • 5 comments

Go version

go1.22.0.linux-amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/user/.cache/go-build'
GOENV='/home/user/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/user/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/user/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.0'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/user/myproject/go.mod'
GOWORK='/home/user/myproject/go.work'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build2649934156=/tmp/go-build -gno-record-gcc-switches'

What did you do?

I'm trying to check in the runtime if I can use a value as a key in a map. I'm using reflect.Value.Comparable() to determine whether a value is comparable or not. Example: https://go.dev/play/p/3p7t501BqSU

What did you see happen?

It says my struct isn't comparable, but I still can use it as a key in a map or compare it with other structs.

What did you expect to see?

If this is something that's intended, then at least name of reflect.Value.Comparable() function could be more accurate. Otherwise, I expect this function to return true for any type that can be compared or used as a key for a map.

donwitobanana avatar Feb 15 '24 13:02 donwitobanana

I think that it boils down to this:

https://github.com/golang/go/blob/48d899dcdbed4534ed942f7ec2917cf86b18af22/src/reflect/value.go#L3415-L3416

Simpler reproducer:

var val any = nil
v := reflect.ValueOf(&val).Elem().Elem()
fmt.Println(v.Comparable()) // false
var val any = 5
v := reflect.ValueOf(&val).Elem().Elem()
fmt.Println(v.Comparable()) // true

EDIT: See https://github.com/golang/go/issues/65718#issuecomment-1948167005

mateusz834 avatar Feb 15 '24 14:02 mateusz834

@donwitobanana does the comment by @mateusz834 answer your question?

thanm avatar Feb 15 '24 14:02 thanm

I think nil interface value should probably be considered comparable.

cherrymui avatar Feb 15 '24 22:02 cherrymui

@thanm the comment by @mateusz834 provides simpler reproducer, but unfortunately doesn't answer the question. The concern if this should be considered "comparable" is still valid.

donwitobanana avatar Feb 16 '24 08:02 donwitobanana

@mateusz834's reproducer is not correct. The following is the correct one:

package main

import "reflect"

func main() {
	var a any
	v := reflect.ValueOf(&a).Elem()
	println(v.Comparable(), a == a) // false true
}

This is certainly a bug, because reflect result should be consistent with non-reflect one..

zigo101 avatar Feb 16 '24 10:02 zigo101

@go101 Yeah, thanks for catching this

mateusz834 avatar Feb 16 '24 11:02 mateusz834

Change https://go.dev/cl/564795 mentions this issue: reflect: make Value.Comparable return true for nil interface value

gopherbot avatar Feb 16 '24 13:02 gopherbot