Unexpected behaviour in nested DefaultDicts

So what is happening is that

outer = DefaultDict{Int64, DefaultDict}(DefaultDict{Int64, Float64}(1.0))

is the same as:

inner = DefaultDict{Int64, Float64}(1.0)
outer = DefaultDict{Int64, DefaultDict}(inner)

So whenever outer needs a default value,
it returns inner.
Not a copy of inner, or a new DefaultDict equal to inner, but actually inner itself.
Which means that mutating that returned value, is literally mutating inner.


If rather than passing in a value, you pass in a function (or a constructor) as the default,
then DefaultDict will invoke that function and return the value.
You can use this to make it construct a new inner dict each time it wants one.

julia> construct_new_inner() = DefaultDict{Int64, Float64}(1.0);

julia> outer = DefaultDict{Int64, DefaultDict}(construct_new_inner)
DefaultDict{Int64,DefaultDict,var"##9#10"} with 0 entries

julia> outer[1][1]
1.0

julia> outer[1][1] = 4.0
4.0

julia> outer[1][3]
1.0

julia> outer[6][1]
1.0

Which can also be written as:

d = DefaultDict{Int64, DefaultDict}(()->DefaultDict{Int64, Float64}(1.0))
4 Likes