# Converting String to Symbol during struct construction

**URL:** <https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031>\
**Category:** General Usage\
**Tags:** struct, constructors\
**Created:** [January 22, 2022, 12:20pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031 "2022-01-22T12:20:33Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 22, 2022, 12:20pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/1 "2022-01-22T12:20:33Z")

</div>

I am looking at the following

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
end
MyStruct(value::String, kwargs...) = MyStruct(value=Symbol(value), kwargs...)

MyStruct(id=1, value=:a) # this works
MyStruct(id=2, value="b") # this does not work

```

which I naively assumed to be working. But this fails with `MethodError: Cannot `convert` an object of type String to an object of type Symbol`. What would be the proper way to implement a “converting constructor” like that?

* * *

Edit: I am not using a constructor inside the struct, since using `new(...)` does not work with keyword arguments, as far as I did understand. Is that correct, or should I keep the constructor inside the struct?

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [January 22, 2022, 1:05pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/2 "2022-01-22T13:05:44Z")

</div>

Adding a second constructor

```julia
MyStruct(id::Int, value::String, kwargs...) = MyStruct(id=id, value=Symbol(value), kwargs...)

```

makes this work for me.

---

<div class="post-metadata">

**Author:** ![Bardo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bardo/32/21601_2.png) [@Bardo](https://discourse.julialang.org/u/Bardo)\
**Post date:** [January 22, 2022, 1:07pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/3 "2022-01-22T13:07:01Z")

</div>

You could put the conversion into the constructor:

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
end
MyStruct(value::Symbol, kwargs...) = MyStruct(value, kwargs...)

MyStruct(id=2, value=Symbol(:a))
MyStruct(id=2, value=Symbol("b"))

```

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 22, 2022, 1:33pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/4 "2022-01-22T13:33:52Z")

</div>

> [@goerch](#):
>
> Adding a second constructor
> 
> ```julia
> MyStruct(id::Int, value::String, kwargs...) = MyStruct(id=id, value=Symbol(value), kwargs...)
> 
> ```
> 
> makes this work for me.

This seems to only work due to essentially eliminating `kwargs...` in the MWE. Consider the following change, that breaks it again:

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
    notimportant::Int = -1
end
MyStruct(id::Int, value::String, kwargs...) = MyStruct(id=id, value=Symbol(value), kwargs...)

MyStruct(id=1, value="a") # this fails again

```

* * *

> [@Bardo](#):
>
> You could put the conversion into the constructor:
> 
> ```julia
> Base.@kwdef mutable struct MyStruct
> id::Int
> value::Symbol = :fixed
> end
> MyStruct(value::Symbol, kwargs...) = MyStruct(value, kwargs...)
> 
> MyStruct(id=2, value=Symbol(:a))
> MyStruct(id=2, value=Symbol("b"))
> 
> ```

While this certainly works, it achieves something else and does not solve the initial problem. It converts the `String` to a `Symbol` **ahead** of construction of the `struct`, instead of **during**. While this seems like a valid fix, consider the following:

- I have a large “data storage” (the `struct`), that is initialized using data parsed from a file (yaml, csv, …). I read in `String`s. This is fairly efficient for most fields. Later on I come to a point where profiling shows that keeping a specific field as `Symbol` makes sense (due the timeloss of doing `Symbol("somestring")` after reading the data, using `Symbol`s can be worse than `String`s for many fields).
- I construct this `struct` at several points of my code. If I need to “symbolify” a new field, I need to change this everywhere instead of at a single central point (the constructor).

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 22, 2022, 1:36pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/5 "2022-01-22T13:36:42Z")

</div>

> [@sstroemer](#):
>
> ```julia
> MyStruct(value::String, kwargs...) = MyStruct(value=Symbol(value), kwargs...)
> 
> ```

In this method, `value` is a _positional_ argument, not a keyword argument. And your `kwargs...` are actually positional arguments as well. In Julia, unlike Python, keyword arguments are totally distinct from positional arguments.

In consequence, `MyStruct(id=2, value="b")` does not call this method, and instead calls the lower-level inner constructor of `MyStruct`, which takes its arguments and calls `convert`, the same as when you do assignment with `=`. There is no `convert` method from `String` to `Symbol` (Julia only defines `convert` when you want conversion to happen implicitly), hence the error.

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [January 22, 2022, 1:40pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/6 "2022-01-22T13:40:59Z")

</div>

> [@sstroemer](#):
>
> Consider the following change, that breaks it again:

Maybe this helps then

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
    notimportant::Int = -1
    function MyStruct(id, value, notimportant)
        new(id, Symbol(value), notimportant)
    end
end

MyStruct(id=1, value=:a)       
MyStruct(id=2, value="b")      

```

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 22, 2022, 1:47pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/7 "2022-01-22T13:47:52Z")

</div>

> [@stevengj](#):
>
> In this method, `value` is a _positional_ argument, not a keyword argument. And your `kwargs...` are actually positional arguments as well. In Julia, unlike Python, keyword arguments are totally distinct from positional arguments.

I’ve previously tried this:

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
end
MyStruct(; value::String, kwargs...) = MyStruct(; value=Symbol(value), kwargs...)

MyStruct(; id=1, value="a") # fails

```

Which fails with `TypeError: in keyword argument value, expected String, got a value of type Symbol`. Your comment suggests that this would be the right direction, but something with my approach is wrong.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 22, 2022, 1:55pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/8 "2022-01-22T13:55:08Z")

</div>

> [@sstroemer](#):
>
> `MyStruct(; value::String, kwargs...`)

Julia [doesn’t dispatch on keyword arguments](https://docs.julialang.org/en/v1/manual/methods/#Note-on-Optional-and-keyword-Arguments), so this won’t work:

> Keyword arguments behave quite differently from ordinary positional arguments. In particular, they do not participate in method dispatch. Methods are dispatched based only on positional arguments, with keyword arguments processed after the matching method is identified.

See the solution by @goerch [above](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/6). Or you could just write a totally new function, e.g. `mystruct(...) = MyStruct(...)`.

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 22, 2022, 2:35pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/9 "2022-01-22T14:35:36Z")

</div>

> [@goerch](#):
>
> Maybe this helps then
> 
> ```julia
> Base.@kwdef mutable struct MyStruct
> id::Int
> value::Symbol = :fixed
> notimportant::Int = -1
> function MyStruct(id, value, notimportant)
> new(id, Symbol(value), notimportant)
> end
> end
> 
> MyStruct(id=1, value=:a)       
> MyStruct(id=2, value="b")      
> 
> ```

Thanks for that, but I am really looking for a way to utilize the `kwargs` iterator - this is after all a feature of the language and manually adding tens (or hundreds) of variables to the constructor is not how I feel it “should” work.

* * *

> [@stevengj](#):
>
> Julia [doesn’t dispatch on keyword arguments](https://docs.julialang.org/en/v1/manual/methods/#Note-on-Optional-and-keyword-Arguments), so this won’t work:

This was the important information I somehow missed. Without introducing a function with a different name, this allows the conversion by utilizing an “optional” positional argument like:

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::Symbol = :fixed
end
MyStruct(::Type{String}; value, kwargs...) = MyStruct(; value=Symbol(value), kwargs...)

MyStruct(String; id=1, value="a")
MyStruct(; id=1, value=:a)

```

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 22, 2022, 3:30pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/10 "2022-01-22T15:30:54Z")

</div>

You can also have the conversion happen automatically, without specifying extra type arguments. But I think this requires getting rid of the automatic `Base.@kwdef` kwargs constructor. For example:

```julia
julia> struct MyStruct
           id::Int
           value::Symbol
       end

julia> MyStruct(; value, kws...) = MyStruct((value=Symbol(value), kws...)[fieldnames(MyStruct)])

```

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 22, 2022, 5:28pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/11 "2022-01-22T17:28:13Z")

</div>

> [@aplavin](#):
>
> You can also have the conversion happen automatically, without specifying extra type arguments. But I think this requires getting rid of the automatic `Base.@kwdef` kwargs constructor.

But then I lose out on using default values for the `struct`s fields. Or is there an alternative way (besides packing them into the constructor)?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 22, 2022, 6:34pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/12 "2022-01-22T18:34:06Z")

</div>

> [@sstroemer](#):
>
> Or is there an alternative way (besides packing them into the constructor)?

You could define your own symbol-wrapper type with a `convert` method:

```julia
struct StringSymbol
    sym::Symbol
end
StringSymbol(s::AbstractString) = StringSymbol(Symbol(s))
Base.Symbol(s::StringSymbol) = s.sym
Base.convert(::Type{StringSymbol}, s::Union{Symbol,AbstractString}) = StringSymbol(s)

```

Then you can do:

```julia
Base.@kwdef mutable struct MyStruct
    id::Int
    value::StringSymbol = :fixed
end

```

and get

```julia
julia> MyStruct(id=1, value=:a)
MyStruct(1, StringSymbol(:a))

julia> MyStruct(id=2, value="b")
MyStruct(2, StringSymbol(:b))

```

(The compiled code will be equivalent to code with a `Symbol` field — everything is inlined. You will probably need to define a few more methods on `StringSymbol` depending on how you use it, to let it be a drop-in replacement for `Symbol`.)

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 22, 2022, 8:36pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/13 "2022-01-22T20:36:07Z")

</div>

It doesn’t really seem possible to make a user-defined type behave like a Symbol. For example:

```julia
julia> s = :abc
julia> ss = StringSymbol(:abc)

julia> (;[s => 123]...)
(abc = 123,)
# but
julia> (;[ss => 123]...)
ERROR: TypeError: in typeassert, expected Symbol, got a value of type StringSymbol

```

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 22, 2022, 9:50pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/14 "2022-01-22T21:50:45Z")

</div>

> [@aplavin](#):
>
> It doesn’t really seem possible to make a user-defined type behave like a Symbol. For example:

AFAIK, there are pretty few contexts in Julia that really require a `Symbol`; basically anything that smells like metaprogramming, e.g. programmatic generation of keyword arguments as in your example.

In @sstroemer’s case, they don’t seem to be doing this as it sounds like they are just using symbols as [interned](https://en.wikipedia.org/wiki/String_interning) replacements for strings.

Alternatively, it sounds like they may just want something like [GitHub - JuliaString/InternedStrings.jl: Fully transparent string interning functionality, with proper garbage collection · GitHub](https://github.com/JuliaString/InternedStrings.jl) … that package has suffered from a lack of attention, but it might be worth a little loving care as an alternative for cases where people want to use symbols as fast interned strings?

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [January 23, 2022, 1:19am UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/15 "2022-01-23T01:19:52Z")

</div>

> [@stevengj](#):
>
> You could define your own symbol-wrapper type with a `convert` method:

I’m not sure if that comes at a huge disadvantage somewhere else, but taking your idea and just applying it to the problem of the missing “automatic conversion” from `String` to `Symbol` leads to:

```julia
Base.@kwdef mutable struct MyStruct
    value::Symbol = :a
end

Base.convert(::Type{Symbol}, s::String) = Symbol(s)

MyStruct() # works
MyStruct(:b) # works
MyStruct("c") # works

```

Am I missing something, or is that an easy solution to the problem, without any major downsides?!

* * *

> [@stevengj](#):
>
> In @sstroemer’s case, they don’t seem to be doing this as it sounds like they are just using symbols as [interned](https://en.wikipedia.org/wiki/String_interning) replacements for strings.

This is right, I am currently using `Symbol`s in the `struct` to allow for a faster check for equality (after a few `mystruct.value == "something"` checks, the cost of initially “symbolifying” the `String` is outweighed by the gain in speed on the comparison).  
But that would mean, if the above approach is okay, I could just change the type of **any** field, of **any** struct, at **any** time from `String` to `Symbol`, without doing anything else and interning would automatically happen. That sounds like an extremely convenient/elegant solution!?

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 23, 2022, 1:22pm UTC](https://discourse.julialang.org/t/converting-string-to-symbol-during-struct-construction/75031/16 "2022-01-23T13:22:53Z")

</div>

> [@sstroemer](#):
>
> without any major downsides?!

A major downside of using `Symbol` as an interned string is that it will never be garbage-collected. That’s an advantage of the InternedString.jl approach (which uses a `WeakKeyDict` to store a garbage-collectable cache).
