'Initializing a struct field that is a vector in Julia

I have the following mutable struct:

mutable struct foo
    x::Vector{String}
    # other field(s)...

    # constructor(s)
end

I would like to create an object from this struct and edit it like the following:

bar = foo()  # or some other constructor
push!(bar.x, "a")
# Do some stuff...
push!(bar.x, "b")
# Do some more stuff...
push!(bar.x, "c")

To do this, what is the best constructor for foo?


P.S. I have tried the following constructors:

foo() = new()

If I use this and do push!(bar.x, "a"), I get an UndefRefError. I could initialize bar.x outside (like bar.x = []), but I really want to avoid this.

I have also tried:

foo() = new([])

With this, I can do the push operations without a problem. However, if I have many other other fields in the struct that are also vectors, I would have to do this:

mutable struct foo
    x::Vector{String}
    y::Vector{String}
    z::Vector{String}
    w::Vector{String}
    # maybe dozens more...

    foo() = new([], [], [], [], ...)  # kind of ugly and wastes time to type

end

Is this the best there is?



Solution 1:[1]

You can do:

julia> mutable struct Foo
           x::Vector{String}
           Foo() = new(String[])
       end

julia> Foo()
Foo(String[])

However, for your scenario the most convenient would be Base.@kwdef:

Base.@kwdef mutable struct FooB
    x::Vector{String} = String[]
    y::Vector{String} = String[]
end

Now of course you can do:

julia> FooB()
FooB(String[], String[])

However other methods are available too:

julia> methods(FooB)
# 3 methods for type constructor:
[1] FooB(; x, y) in Main at util.jl:478
[2] FooB(x::Vector{String}, y::Vector{String}) in Main at REPL[9]:2
[3] FooB(x, y) in Main at REPL[9]:2

So you could do:

julia> FooB(;y=["hello","world"])
FooB(String[], ["hello", "world"])

Solution 2:[2]

There's one alternative that is quite concise and almost as fast as writing out the inputs:

struct Foo  # Typenames should be uppercase
    x::Vector{String}
    y::Vector{String}
    z::Vector{String}
    w::Vector{String}
    Foo() = new((String[] for _ in 1:4)...) # using a generator and splatting
    # Foo() = new(ntuple(_->String[], 4)...) # also possible
end

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Przemyslaw Szufel
Solution 2 DNF