-
Notifications
You must be signed in to change notification settings - Fork 18k
reflect: document behaviour of DeepEqual when applied to cyclic data #20428
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
Comments
I think it is predictable that the comparisons all return true: package main
import (
"fmt"
"reflect"
)
func main() {
type box struct{ n *box }
var b = &box{&box{&box{&box{nil}}}}
b.n.n.n.n = b.n.n
// all return true
fmt.Println(reflect.DeepEqual(b, b.n))
fmt.Println(reflect.DeepEqual(b, b.n.n))
fmt.Println(reflect.DeepEqual(b.n, b.n.n.n))
fmt.Println(reflect.DeepEqual(b.n.n, b.n.n.n.n))
} But, in the following example, the two false results are not expected: package main
import (
"fmt"
"reflect"
)
func main() {
type P *P
var p P
p = &p
fmt.Println(p, &p, *p) // three same addresses
fmt.Println (p == &p) // true
fmt.Println(reflect.DeepEqual(p, &p)) // false
fmt.Println (p == *p) // true
fmt.Println(reflect.DeepEqual(p, *p)) // true
fmt.Println (&p == *p) // true
fmt.Println(reflect.DeepEqual(&p, *p)) // false
} |
For the cases where we are comparing
That's just because type box struct{ n *box }
var b = &box{&box{&box{&box{nil}}}}
b.n.n.n.n = b.n.n
fmt.Printf("%p %p\n", b.n, b.n.n.n) // not the same addresses
fmt.Println(b.n == b.n.n.n) // false, unsurprisingly
They are, if you look at the types: type P *P
var p P
p = &p
fmt.Printf("%p (%[1]T) %p (%[2]T) %p (%[3]T)\n", p, &p, *p) // three same addresses I will admit to being a little surprised to realise that P and *P are not the same type, but upon reflection (no pun intended) that is consistent with the usual behaviour: if we say |
One more confused is the results printed by package main
import (
"fmt"
)
func main() {
type box struct{ n *box }
var b = &box{&box{&box{&box{nil}}}}
b.n.n.n.n = b.n.n
fmt.Println(b.n, b.n.n, b.n.n.n)
println(b.n, b.n.n, b.n.n.n)
// output:
// &{0xc42000c030} &{0xc42000c038} &{0xc42000c030}
// 0xc42000c028 0xc42000c030 0xc42000c038
} @cpcallen, Yes, the types of |
The documentation for |
Didn't read the DeepEqual docs carefully. |
Change https://golang.org/cl/52931 mentions this issue: |
The documentation for reflect.DeepEqual in Go 1.8 doesn't clearly specify what happens when it is passed data containing cycles. A strict reading of the documentation might suggest that in some cases it will never terminate. Consider the following scenario:
I will admit to naïveté in being surprised that
reflect.DeepEqual(b, b.n.n)
not only terminates but returnstrue
despiteb
andb.n.n
being structurally different: althoughb != b.n.n
andb.n != b.n.n.n
, we eventually find thatb.n.n == b.n.n.n.n
, so they are indeed deeply equal according to the definition given.However, the fact that
reflect.DeepEqual(b, b.n)
also returns returnstrue
is quite extraordinary and does not seem to be predictable from the definition given in the documentation: at no point do we get pointer equality, and it is not clear on what other basis it can be that "they point to deeply equal values".(I am not proposing that the behaviour of
DeepEqual
be changed, even if this surprising behaviour makes it unsuitable for my particular application; I merely think the documentation should be clear about the expected behaviour.)The text was updated successfully, but these errors were encountered: