Surprising struct equality test

By my understanding of structs, they should be compared by value by default. So what is going here??

struct T
    name::String
end

T("hello") == T("hello")

returns false!

4 Likes

The fields of a struct are compared using === (object identity). "hello" === "hello" is false. But you’re not alone in finding the behaviour counterintuitive:

https://github.com/JuliaLang/julia/issues/4648

4 Likes

This is because

julia> "hello" === "hello"
false

Mutable members compares through object identity by default.

1 Like

Is there a way to modify this behavior for my struct? Can I “redefine” equality somehow?

2 Likes

Hmmm, I am confused. Why isn’t "hello" === "hello"? Why strings don’t behave like values?

2 Likes

You can redefine Base.:(==), but it may break some implicit assumptions in existing code, also, you need to define hashing. For the moment, you are better off defining your own equality operator. I posted an example in a parallel topic.

3 Likes

Thanks. I did this:

Base.:(==)(x::T, y::T) = x.name == y.name

Why do you say that could “break some implicit assumptions in existing code”?
And you think I have to define hashing in this simple example (my type is literally this simple)? How?
Thanks!

1 Like

A function could rely on existing behavior of ==. If you change that, you could break code.

Your code is nonsensical, and won’t even compile.

As for hashing, see the manual. Or as an example,

3 Likes

What do you mean it won’t compile? I have it right here ant it works. Do you get an error??

Note that I am defining == only for my type T … I don’t see the danger, maybe I am missing something?

2 Likes

I thought T was a type parameter, which should have required a where T. If it is a concrete type, which has a field name, then it should work.

1 Like

Ah yes, sorry. I was using the T I defined in the first post.

This is very unintuitive:

struct T1 s::String end
Base.:(==)(x::T1, y::T1) = x.s == y.s

struct T2 i::Int end
struct P x::T1 end
struct Q x::T2 end

@show T1("hola") == T1("hola");   # true
@show P(T1("hola")) == P(T1("hola"));  # false (!)
@show Q(T2(1)) == Q(T2(1));  # true (wtf)

How can I fix this? I want my T1 to behave like a true value. Overloading == on T1 is not enough, as you can see from this example. I would have to re-define == on P as well (and all composite types that contain T1, which is impractical). And overloading === of T1 seems to be forbidden (I get an error).

@Tamas_Papp @cstjean @yuyichao (help me :slight_smile: )

1 Like

The answer is in the very first response you got. Also, note that pinging people for no good reason is considered impolite.

Sorry, but I do not see how that fixes the problem? I mean a workaround to make T1 behave as a true value type, in spite of the string field. Care to explain?

1 Like

Strings are for implementation reasons defined as a mutable struct and therefore use object equality for ===.

Are ther eplans to fix this issue for v1.0?

0.7:

julia> "hello" === "hello"
true

julia> struct T
           name::String
       end

julia> T("hello") == T("hello")
true
2 Likes

To make sure I understand this correctly (I’ve already got confused by this issue in the past): the counter-intuitive T("Hello") != T("Hello") was fixed, but not by making == call the various fields recursively (meaning a == b iff a.f == b.f for every field). Instead what changed is that now the same string literals are ===?

3 Likes

Yes.