Comparing mutable structs with identical field values in Julia

Description

I am working on a project that involves comparing mutable structs with identical field values in Julia. However, I am encountering unexpected results when using the == and === operators, as they return false for mutable structs with identical field values. According to the Julia documentation, the == operator falls back to === for structs.

Here’s a minimal reproducible example:

mutable struct MutRange
	start::Int
	finish::Int
end

println(MutRange(1, 3) === MutRange(1, 3)) # false
println(MutRange(1, 3) == MutRange(1, 3)) # false

Based on the documentation at Mathematics · The Julia Language, the == operator falls back to === for structs. However, in this case, both comparisons return false.

This issue is reported at GitHub issues.

Could someone please provide guidance on how to compare mutable structs correctly with identical field values in Julia? I appreciate any help and expertise you can provide.

Thank you in advance for your assistance!

1 Like

One way is to define your custom comparison operator, for your type. This is probably the safest way:

julia> mutable struct MutRange
               start::Int
               finish::Int
       end

julia> import Base: ==

julia> ==(x::MutRange, y::MutRange) = x.start == y.start && x.finish == y.finish
== (generic function with 195 methods)

julia> MutRange(1,3) == MutRange(1,3)
true

You can make this more general with:

julia> ==(x::MutRange, y::MutRange) = all(getfield(x, field) == getfield(y, field) for field in fieldnames(MutRange))
== (generic function with 195 methods)

julia> MutRange(1,3) == MutRange(1,3)
true
2 Likes

=== checks object identity. So

x = MutRange(1, 3)
y = MutRange(1, 3)

Then x === x and y === y are true, but x === y is false. If you haven’t defined == it will just call ===

If you want to overload equality to fit your needs there are various packages for this, see GitHub - jw3126/StructHelpers.jl: Automate common struct definition boilerplate

If you don’t want to overload equality, you can do:

using ConstructionBase
getproperties(obj1) == getproperties(obj2)

Thank you for your helpful suggestion! Defining a custom comparison operator for my MutRange struct solved the issue. Here’s my updated code:

mutable struct MutRange
	start::Int
	finish::Int
end

import Base: ==

==(x::MutRange, y::MutRange) = x.start == y.start && x.finish == y.finish

println(MutRange(1, 3) === MutRange(1, 3)) # false
println(MutRange(1, 3) == MutRange(1, 3)) # true

This custom comparison operator ensures that mutable structs with identical field values are correctly compared. I appreciate your expertise and assistance in resolving this problem.

Alternatively, you can define a function like this:

import Base: ==

function ==(a::MutRange, b::MutRange)
	return a.start == b.start && a.finish == b.finish
end

This function can be used to compare MutRange instances similarly to the custom comparison operator. The custom comparison operator is defined as an anonymous function within the import Base: == statement, which may be unfamiliar to new Julia users. The alternative function provides a more explicit way to define the comparison operator.

Maybe it is too much for this case, but I like this package for this: GitHub - JuliaServices/AutoHashEquals.jl: A Julia macro to add == and hash() to composite types.