Custom hash not called for field in another struct – expected?

The Julia docs say:

But when I do this, it seems to ignore my custom hash function in certain contexts:

julia> struct Foo
       end

julia> Base.hash(::Foo, h::UInt) = error()

The following calls are what I would expect:

julia> hash(Foo())
ERROR:
Stacktrace:
 [1] error()
   @ Base ./error.jl:44
 [2] hash(::Foo, h::UInt64)
   @ Main ./REPL[2]:1

julia> hash((Foo(),))
ERROR:
Stacktrace:
 [1] error()
   @ Base ./error.jl:44
 [2] hash(::Foo, h::UInt64)
   @ Main ./REPL[2]:1

But this one is not expected:

julia> struct Bar
           x::Foo
       end

julia> hash(Bar(Foo()))
0xb4bfb67e8815404c

Is there a reason why the default hash on structs does not recursively travel through fields? Does this mean if I implement a custom hashing function for my type, I also need to overload hash for any type that takes it as a property to ensure my hash gets hit?

The default hash is objectid (matching the default == which is ===) which doesn’t look at the fields.

see also: Convenient ways to define == and hash · Issue #53565 · JuliaLang/julia · GitHub

Thanks!

Maybe it’s just me but the default == and hash feel kinda unintuitive, no? I know it won’t change, just wondering why my mental model is broken

julia> mutable struct A
           a::Float64
       end

julia> x = A(1.0)
A(1.0)

julia> hash(deepcopy(x))
0x9e99cc3918a450f2

julia> hash(x)
0x3f7020c38a5b2e12

julia> deepcopy(x) == x
false

I guess I just never think of a “hash” as being sensitive to the particular location in memory.

Obviously this doesn’t matter for immutables but still

2 Likes

Ah, there’s even an issue for it. Good to know it’s not just me.

3 Likes