Problems overloading setindex!()


I’m running into obstacles trying to overload setindex!() for a dict wrapper type. I’m probably missing something obvious here but I can’t figure out what exactly.

I’m getting an error when adding a Consumable which is an implementation of the abstract type Entity.

abstract type Entity end

struct Entities
    Entities() = new(Dict{Blueprint,Vector{Entity}}())

Base.keys(entities::Entities) = keys(entities.entities)
Base.values(entities::Entities) = values(entities.entities)
Base.getindex(entities::Entities, index::Blueprint) = entities.entities[index]
Base.setindex!(entities::Entities, e::Array{Entity,1}, index::Blueprint) = (entities.entities[index] = e[1])
Base.setindex!(entities::Entities, e::Entity, index::Blueprint) = (entities.entities[index] = e)

function Base.push!(entities::Entities, entity::Entity)
    if entity.blueprint in keys(entities)
        push!(entities[entity.blueprint], entity)
        entities[entity.blueprint] = Vector{Entity}([entity])

    return entities

function Base.pop!(entities::Entities, blueprint::Blueprint)
    if blueprint in keys(entities)
        return pop!(entities[blueprint])
        return nothing

struct Consumable <: Entity
    Consumable(blueprint) = new(uuid4(), blueprint, SingleUse())

I tried leaving out one of the implementations of setindex!() but in both cases I get an error. If I leave out the first implementation I get:

ERROR: MethodError: no method matching setindex!(::Entities, ::Array{Entity,1}, ::ConsumableBlueprint)
Closest candidates are:
  setindex!(::Entities, ::Entity, ::Blueprint) at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:80
 [1] put!(::Entities, ::Consumable) at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:86
 [2] top-level scope at none:1

And if I add that implementation I get:

ERROR: MethodError: Cannot `convert` an object of type Consumable to an object of type Array{Entity,1}
Closest candidates are:
  convert(::Type{T}, ::AbstractArray) where T<:Array at array.jl:554
  convert(::Type{T}, ::T) where T<:AbstractArray at abstractarray.jl:14
  convert(::Type{T}, ::LinearAlgebra.Factorization) where T<:AbstractArray at /Users/julia/buildbot/worker/package_macos64/build/usr/share/julia/stdlib/v1.5/LinearAlgebra/src/factorization.jl:55
 [1] setindex!(::Dict{Blueprint,Array{Entity,1}}, ::Consumable, ::ConsumableBlueprint) at ./dict.jl:380
 [2] setindex!(::Entities, ::Array{Entity,1}, ::ConsumableBlueprint) at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:79
 [3] put!(::Entities, ::Consumable) at /Users/stef/Programming/Julia Projects/loreco-abm/cockpit/production/entities.jl:86
 [4] top-level scope at none:1

Thanks in advance,

Base.setindex!(entities::Entities, e::Array{Entity,1}, index::Blueprint) = (entities.entities[index] = e[1])

I find this a bit weird, as you dispatch on Array{Entity,1} just to only extract the first element. What happens if there are two elements in the vector? Why do you need this method in the first place if you only expect one element? Then at least I would do entities.entities[index] = only(e) so it errors if that assumption is violated.

The error comes from the fact that your entities dict stores objects of type Vector{Entity}, but you specifically instruct to extract the element from the Vector{Entity} that you have, which means you pass the wrong type. The other setindex! method also passes an Entity, and not a Vector of them.

I have no idea what the code is supposed to do, but maybe your issue goes away if you do:

Base.setindex!(entities::Entities, e::Array{Entity,1}, index::Blueprint) = (entities.entities[index] = e) # pass a vector
Base.setindex!(entities::Entities, e::Entity, index::Blueprint) = (entities.entities[index] = [e]) # pass a vector

Thanks! That solved it! And now I can see what I was overlooking. I only need 1 setindex!() now to make it work: the first one you wrote.