-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comparing reflect types behaves not as expected #80
Comments
When comparing pointers in compareAny, if the pointers are pointing on the same object we report that they are equal. Fixes google#80
When comparing pointers in compareAny, if the pointers are pointing on the same object we report that they are equal. Fixes google#80
This is behaving as intended. The user should be explicit about the proper way to compare cmp.Comparer(func(x, y reflect.Type) bool { return x == y }) Sometimes pointer comparisons are the right approach, other times the right approach is to descend into the value. We chose the current behavior for the following reasons:
|
Here's an example of how an option can be used to obtain the equivalent behavior of func EquateIdenticalPointers() cmp.Option {
return cmp.FilterPath(func(p cmp.Path) bool {
// Filter for pointer kinds only.
t := p.Last().Type()
return t != nil && t.Kind() == reflect.Ptr
}, cmp.FilterValues(func(x, y interface{}) bool {
// Filter for pointer values that are identical.
vx := reflect.ValueOf(x)
vy := reflect.ValueOf(y)
return vx.IsValid() && vy.IsValid() && vx.Pointer() == vy.Pointer()
}, cmp.Comparer(func(_, _ interface{}) bool {
// Consider them equal no matter what.
return true
})))
} Note that this option will conflict with another option that is applicable on *T. See #36 for a proposed solution around that. With #36, you could always wrap inside return cmp.Diff(x, y,
DeepAllowUnexported(x ,y),
cmp.FilterOrder(
cmp.Options(userOpts), // User-provided options take precedence
EquateIdenticalPointers(),
),
) |
I see One last thing that is not very clear to me. {*reflect.rtype}.alg.hash:
-: (func(unsafe.Pointer, uintptr) uintptr)(0x451530)
+: (func(unsafe.Pointer, uintptr) uintptr)(0x451530)
{*reflect.rtype}.alg.equal:
-: (func(unsafe.Pointer, unsafe.Pointer) bool)(0x401a50)
+: (func(unsafe.Pointer, unsafe.Pointer) bool)(0x401a50) |
Both of those are function pointers. Functions in Go are not comparable (as defined in the language spec). In this situation, Even though both appear to have the same pointer, that is actually an implementation detail of the current Go compiler. For top-level functions, a pointer has a well-defined meaning. However, when you derive a function pointer from a method or closure, the meaning becomes more hazzy. func MakeClosure() func() int {
var x int
return func() {
x++
return x
}
}
f1 := MakeClosure()
f2 := MakeClosure()
f1 == f2 // Suppose this were possible, what should this return? |
@dsnet The simple function pointer works, closure is not. |
That seems highly suspect. See golang/go#18871. Comparing the |
When comparing two equal types, the response is false, for example, the following code print
false
:See full code
Just to clarify, if the diff is being printed, this is the reason that those two types are not the same:
This is being solved in the standard
refelect.DeepEqual
when comparing two pointers.If the
compareAny
will do the same trick, the above code will printtrue
as expected. It also does not break any of the tests:I am not sure why this comparison is not done in the
go-cmp
implementation, I can't see any harm in doing it, it can also saves running time.The text was updated successfully, but these errors were encountered: