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 Symbol
s 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