Enums to string & string to enum: a simple guide to use enums & strings

Practical guide to Julia Enums ↔ Strings

Well, I do this post for the me of the past that spent way to much time on understanding Enums and how to deal with Symbols, Strings and Enums. Coming from Rust, dealing with Julia Enums was a bit frustrating at first.

Enums are basically wrapped integers for some predefined values.

julia> @enum MyEnum A B C

julia> A
A::MyEnum = 0

julia> B
B::MyEnum = 1

julia> C
C::MyEnum = 2

Here, MyEnum is a type derived from Enum (Type{<:Enum}), and A, B, and C are values of that Enum type.


2. Converting Between Enums, Strings, and Symbols

Julia does not provide built-in conversion functions between Enums and Strings, so knowing which method to call is crucial.

Convert an Enum Value to a String

julia> string(Symbol(A))
"A"

Convert an Enum Type to a String Listing All Possible Values

julia> join(string.(instances(MyEnum)), ", ")
"A, B, C"

Convert a String to an Enum Value

julia> eval(Symbol("A"))
A::MyEnum = 0

3. Using Enums in Command-Line Argument Parsing

A common use case is handling program arguments where we need to parse Enum values from command-line inputs and provide users with available options in --help. Below is a practical example using ArgParse.jl.

Example: Parsing Command-Line Arguments with Enums

using ArgParse
using Logging

@enum MyEnum A B C
@enum TestEnum begin
    TestA
    TestB
    TestC
end

function enum_values_to_string(enum::Type{<:Enum})::String
    return join(string.(instances(enum)), ", ")
end

function enum_to_string(enum_value::Enum)::String
    return string(Symbol(enum_value))
end

function string_to_enum(str::String)::Enum
    return eval(Symbol(str))
end

function test_enum_and_strings()
    @info "Enum values: $(enum_values_to_string(TestEnum))"
    @info "Enum to string: $(enum_to_string(A)) of type $(typeof(enum_to_string(A)))"
    @info "String to enum: $(string_to_enum("A")) of type $(typeof(string_to_enum("A")))"
end

"Parse program arguments and return the result as a Dict."
function parse_program_args(args)
    # Initialize settings
    s = ArgParseSettings(description = "Program arguments to Enum values.")

    @add_arg_table! s begin
    "--log-level", "-l"
        help = "Logging level from " * join([string(level) for level in [Debug, Info, Warn, Error]], ", ")
        arg_type = Symbol
        default = :Info
    "--my-enum", "-m"
        help = "My enum from " * enum_values_to_string(MyEnum)
        arg_type = Symbol
        default = :A
    "--test-enum", "-t"
        help = "Test enum from " * enum_values_to_string(TestEnum)
        arg_type = Symbol
        default = :TestA
    end

    pargs = parse_args(args, s)
    return Dict(
        key => (val isa Symbol ? eval(val) : val) for (key, val) in pargs
    )
end

# Initialize logger with log level from arguments
function initialize_logger(log_level::LogLevel)
    global_logger(ConsoleLogger(log_level))
end

# Example usage of logger
function main(args)
    parsed_args = parse_program_args(args)
    initialize_logger(parsed_args["log-level"])
    @debug "Logger initialized"

    test_enum_and_strings()

    # Print parsed arguments
    @info "Parsed arguments:"
    for (key, val) in parsed_args
        @info "$key: $val of type $(typeof(val))"
    end
end

main(ARGS)

How This Works:

  1. enum_values_to_string(MyEnum): Converts an Enum type into a string listing all possible values.
  2. enum_to_string(A): Converts an Enum value into a string.
  3. string_to_enum("A"): Converts a string back into an Enum value.
  4. parse_program_args(args): Uses ArgParse.jl to handle Enums in command-line arguments.
  5. The main function initializes a logger and processes command-line arguments.

Example Usage in the Terminal

julia enums.jl -h

Output:

usage: enums.jl [-l LOG-LEVEL] [-m MY-ENUM] [-t TEST-ENUM] [-h]

Program arguments to Enum values.

optional arguments:
  -l, --log-level LOG-LEVEL
                        Logging level from Debug, Info, Warn, Error
                        (type: Symbol, default: :Info)
  -m, --my-enum MY-ENUM
                        My enum from A, B, C (type: Symbol, default:
                        :A)
  -t, --test-enum TEST-ENUM
                        Test enum from TestA, TestB, TestC (type:
                        Symbol, default: :TestA)
  -h, --help            show this help message and exit

Another example:

julia enums.jl -l Debug -m B

Output:

┌ Debug: Logger initialized
└ @ Main ~/code/phd/phd_plots/src/enums.jl:66
[ Info: Enum values: TestA, TestB, TestC
[ Info: Enum to string: A of type String
[ Info: String to enum: A of type MyEnum
[ Info: Parsed arguments:
[ Info: log-level: Debug of type Base.CoreLogging.LogLevel
[ Info: test-enum: TestA of type TestEnum
[ Info: my-enum: B of type MyEnum

Conclusion

Enums in Julia are lightweight and efficient but require some manual conversions when dealing with Strings and Symbols. By implementing helper functions, we can seamlessly integrate Enums into applications, particularly for command-line parsing.

Note that many discussions and packages are trying to deal with some possible issues you might have, especially is you need to use enum values multiple times for instance and avoid name collision.

See also:
How can I determine the name of an enum value?
Solving the drawbacks of @enum
Encapsulating enum access via dot syntax
ANN: EnumX.jl – improved enums for Julia

If you have other ways to work with Enums in Julia efficiently, feel free to share! :rocket:

1 Like