Why is this function expecting a string instead of accepting an integer?

Hi everyone. I have this simple function here that doesn’t behave the way I’m expecting:

function build_person(first_name, last_name, age=nothing)
    # Return a dictionary of information about a person.
    person = Dict("first" => first_name, "last" => last_name)
    if !isnothing(age)
        person["age"] = age
    end
    return person
end

When I run this code, passing string values for both first_name and last_name and an integer for age, I get the following error:

ERROR: LoadError: MethodError: Cannot `convert` an object of type Int64 to an object of type String
Closest candidates are:
  convert(::Type{String}, ::String) at essentials.jl:210
  convert(::Type{T}, ::T) where T<:AbstractString at strings/basic.jl:231
  convert(::Type{T}, ::AbstractString) where T<:AbstractString at strings/basic.jl:232
  ...
Stacktrace:
 [1] setindex!(h::Dict{String, String}, v0::Int64, key::String)
   @ Base .\dict.jl:382
 [2] build_person(first_name::String, last_name::String, age::Int64)
   @ Main d:\#####\person.jl:5
 [3] top-level scope
   @ d:\#####\person.jl:10
in expression starting at d:\#####\person.jl:10

It appears that the function is expecting age to receive a string value, but I don’t understand why.

The Dict constructor is assuming you will always use a String key and String value because that’s all you pass it. I.e. it creates a Dict{String,String}.

Replace Dict with either Dict{String,Any} or Dict{String,Union{String,Int}} depending on how narrow you can be with types on the dict values

4 Likes

Thank you! So, Dict{String, String} is the default value and if I want to use any other types, then I need to specify which. This helps a lot.

Thanks again!

Not quite. It’s not the default, it’s just that all the keys and values you give to the initial constructor are strings. That’s when the dict is created and types defined

1 Like

So, because the first key-value pairs I passed were String => String the dictionary was initialized as Dict{String, String}. Therefore, when I tried adding another pair, the dictionary was looking for another String => String and when it wasn’t, it threw an error. That makes sense.

Thanks again!

4 Likes

This looks like you want a struct:

struct Person
    firstname::String
    lastname::String
    age::Int64
end

introduce(p::Person) = println("Hi! I'm ", p.firstname, " ", p.lastname, ", and I'm ", p.age, " years old.")

person = Person("Gustav", "Vasa", 64)
introduce(person)

By the way, if you are only going to use a small set of keys and don’t expect modifications of the dictionary by user, it is a good idea to use Symbols instead of strings, i.e.

person = Dict(:first => first_name, :last => last_name)

Symbols are basically strings that are not supposed to be subdivided into characters. Because they are “atomic”, the operations with them are faster than with strings. As a bonus, an iterable producing symbol-value pair can be unpacked into function keyword arguments. So, if you have a function

foo(; first, last, age=nothing) = stuff()

you can create a person dict and pass it like

foo(; person...) # note the semicolon to unpack as kwargs