Equality of mutable structs

should this be the case ?

mutable struct X
    x::Int64
end

X(1)==X(1) #false

Yes. If you want to compare mutable structs you have to write a function that compares them element wise yourself. I think what you are comparing here are the pointers to the heap where the two structs are stored, and they are different.

1 Like

If you need a simple way to compare mutable structs, this package might help: GitHub - JuliaServices/AutoHashEquals.jl: A Julia macro to add == and hash() to composite types.

2 Likes

== falls back to ===, which semantically tests for identity, in other words are the 2 arguments bound to the same instance. Since a mutable instance can change its value, even match different instances, its value cannot determine its identity. There isn’t an automatic == for field value comparison because of how complicated it gets in the general case, but there’s macros to help.

2 Likes

Thanks Uwe, Benny

p = pointer_from_objref(X) #  Ptr{Nothing} @0x000001f8aaa5e710
p1 = pointer_from_objref(X(1)) # Ptr{Nothing} @0x000001f8cf9e51f0
p2 = pointer_from_objref(X(1)) # Ptr{Nothing} @0x000001f8ab4ac590


struct Y
    x::Int64
end

pointer_from_objref(Y) # Ptr{Nothing} @0x000001f8cf084cd0
p1=pointer_from_objref(Y(1)) # ERROR: pointer_from_objref cannot be used on immutable objects
p2=pointer_from_objref(Y(1))

Y(1)==Y(1) # true
Get the memory address of a Julia object as a `Ptr`. 
The existence of the resulting `Ptr` will not protect the object 
from garbage collection, so you must ensure that the object
 remains referenced for the whole time that the `Ptr` will be used.

This function may not be called on immutable objects, 
since they do not have stable memory addresses.

I’m interested in the topic and despite the answers I’m not sure I fully understand what goes on behind the scenes.
Even the help documentation for pointer_from_objref() doesn’t clarify the situation for me.

I used the pointer_from_objref() function to “investigate” a bit but I would need someone to explain or direct to documents where it is explained in more detail what happens.

Y is immutable, so pointer_from_objref won’t work. Oftentimes, an immutable type won’t have pointers at all unless its fields hold mutable types. There are some exceptions to that like the unfixed-width String (you point to the heap), but even then you still can’t guarantee the address to be stable. Disregarding implementation for a bit, instantiating an immutable type with the same bits content results in the same instance, semantically. It doesn’t matter if there’s actually multiple copies of Y(1)'s data in working memory, it’s all considered the same instance. This allows some implementation optimizations that mutable types don’t have.

I got this from Discourse long ago. The prinln statement
is there because I use this mostly for CI.

#
# ST1 and ST2 must be the same type TS
#
function structeq(ST1, ST2, TS)
eqok=true
for nf in fieldnames(TS)
gx=getfield(ST1,nf); hx =getfield(ST2,nf)
eqok= ((gx==hx) && eqok)
(gx==hx) || println("Difference in field ",nf)
end
return eqok
end

For example

julia> struct bar
       x::Float64
       n::Int64
       end

julia> ST1=bar(1.0,2); ST2=deepcopy(ST1); ST3=bar(2.0,2);

julia> structeq(ST1,ST2,bar)
true

julia> structeq(ST1,ST3,bar)
Difference in field x
false

and it works for mutable structs.

julia> mutable struct foobar
       x::Float64
       n::Int
       end

julia> ST1=foobar(1.0,2); ST2=deepcopy(ST1); 

julia> structeq(ST1,ST2,foobar)
true

julia> ST2.x=3.0;

julia> structeq(ST1,ST2,foobar)
Difference in field x
false