# Two structs with same fields - how?

**URL:** https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787
**Category:** General Usage
**Created:** [December 12, 2024, 8:35pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787 "2024-12-12T20:35:05Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)
#### Post date: [December 12, 2024, 8:35pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/1 "2024-12-12T20:35:05Z")

</div>

The user interface for my package is as follows. There is a mutable struct called UserProblemDescription. It has ~30 fields, all of which are defined as unions with Missing:

```julia
Base.@kwdef mutable struct UserProblemDescription
    dimension1::Union{Int,Missing} = missing
    <etc>
end

```

The user creates one of these with an empty constructor, and then supplies the data one field at a time:

```julia
udp = UserProblemDescription()
udp.dimension1 = 9
<etc>

```

When the user is done, they invoke the main entry-point of my package. I would like the main entry-point to transfer all the data to a struct with the same field names but with fully concrete types instead of `Union{...Missing}` for clarity and type-stability:

```julia
struct InternalProblemDescription
    dimension1::Int
    <etc>

```

It should throw an error if any field is missing.

This seemingly requires a lot of code. The obvious line-by-line code is fragile because I have to repeat the entire list of fields correctly in three places in the code. Is there a package to carry out this operation or a simpler way to obtain the desired effect?

---

<div class="post-metadata">

### Author: ![BambOoxX](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bambooxx/32/22179_2.png) [@BambOoxX](https://discourse.julialang.org/u/BambOoxX)
#### Post date: [December 12, 2024, 9:04pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/2 "2024-12-12T21:04:26Z")

</div>

This is probably of interest to you [ANN: LazilyInitializedFields.jl - Handling lazily initialized fields in a consistent way](https://discourse.julialang.org/t/ann-lazilyinitializedfields-jl-handling-lazily-initialized-fields-in-a-consistent-way/47770)

---

<div class="post-metadata">

### Author: ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)
#### Post date: [December 12, 2024, 11:43pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/3 "2024-12-12T23:43:24Z")

</div>

How about having the constructor take (nonoptional) keyword arguments? Or a dictionary, or a named tuple? That would make `UserProblemDescription` unnecessary.

Sidenote, a struct having that many fields seems fishy.

---

<div class="post-metadata">

### Author: ![mrufsvold](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mrufsvold/32/31600_2.png) [@mrufsvold](https://discourse.julialang.org/u/mrufsvold)
#### Post date: [December 13, 2024, 12:28am UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/4 "2024-12-13T00:28:43Z")

</div>

Could you unpack it into a NamedTuple for internal use? Like,

```julia
function to_named_tuple(x)
    return (;
        (
            n => getproperty(x, n)
            for n in propertynames(x)
        )...
    )
end

```

Constructing this will be type unstable, but once you pass it into the main body of your inner function, it will be concretely typed everywhere.

---

<div class="post-metadata">

### Author: ![Ralph\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ralph_smith/32/10344_2.png) [@Ralph\_Smith](https://discourse.julialang.org/u/Ralph_Smith)
#### Post date: [December 13, 2024, 5:31am UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/5 "2024-12-13T05:31:22Z")

</div>

Consolidation is one of the main uses for metaprogramming. So here’s a related shot at the original query:

```julia
maybe_wrap(T, cond) = cond ? :(Union{$T,Missing}) : T
for (sn,c,) in (
    (:UserThing, true,),
    (:InternalThing, false,)
)
    e = Expr(:struct, c, sn,
            Expr(:block,
                Expr(Symbol("::"), :i, maybe_wrap(:Int, c)),
                Expr(Symbol("::"), :a, maybe_wrap(:Float64, c)),
            )
    )
    eval(e)
end

function convert(s::UserThing)
    InternalThing((getproperty(s,x) for x in fieldnames(UserThing))...)
end

```

The Boolian flag `c` is for mutability.

But if we step back, ISTM you basically have a table of name-type pairs. Perhaps the user-facing object need not live in the type system. If you put the table in a `Dict` instead, you could still use metaprogramming to create the internal struct but also generate more user-friendly validation checks and even allow for YAML or JSON input.

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [December 13, 2024, 4:33pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/6 "2024-12-13T16:33:09Z")

</div>

I think you should skip the `UserProblemDescription` as a struct entirely. It’s not doing you any favors. If the user doesn’t define every field and of a compatible type, you will just throw an error anyway. I’d accomplish what you ask with a `Dict` like this:

```julia
Base.@kwdef struct InternalProblemDescription
	dimension1::Int
	dimension2::Int
	dimension3::Int
	dimension4::Int
	bigness::Float64
	coolness::Float64
end

InternalProblemDescription(d::AbstractDict) = InternalProblemDescription(; d...) # dict->kw constructor

upd = Dict{Symbol, Any}() # can be more specific than `Any` (ie, `Union{Int, Float64}`) if relevant
upd[:dimension1] = 9
upd[:dimension2] = 3
upd[:dimension3] = 4
upd[:dimension4] = 2
upd[:bigness] = 10.0
upd[:coolness] = 1337.0

InternalProblemDescription(upd)
# InternalProblemDescription(9, 3, 4, 2, 10.0, 1337.0)

InternalProblemDescription(Dict()) # call with one or more fields missing
# ERROR: UndefKeywordError: keyword argument `dimension1` not assigned

```

But personally, I find “supplying the data one field at a time” to be very tedious. I’d probably just use the constructor directly. I’ll also remark that a struct with 30 fields could (should?) probably be organized more carefully. You should consider consolidating related groups of fields into sub-structs that are more understandable. For example, it sounds like you could use `dimensions::NTuple{N, Int}` rather than `dimension1::Int, ..., dimensionN::Int`. Maybe some other sets of fields should get their own `struct` definition entirely.

---

<div class="post-metadata">

### Author: ![Stephen\_Vavasis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stephen_vavasis/32/3389_2.png) [@Stephen\_Vavasis](https://discourse.julialang.org/u/Stephen_Vavasis)
#### Post date: [December 13, 2024, 7:19pm UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/7 "2024-12-13T19:19:43Z")

</div>

Thanks for all the proposed solutions! I adopted the solution posted by mikmoore. I did not previously know that a Base.@kwdef structure could accept a splatted dictionary as a constructor argument; this is a useful technique. I tried LazilyInitializedFields.jl, but it seemed to have the drawback that the user would have start the construction with the statement

```julia
upd = ProblemDescription(uninit, ... , uninit)

```

with the correct number of `uninits`, which did not seem as user-friendly as mikmoore’s solution.

As for some of the other comments: Making an interface that can also accept JSON seems out of reach right now because some of the fields in ProblemDescription are Julia functions, and I don’t know how to encode these as JSON.

As for the “tedium” of supplying fields one at a time, I agree that a single constructor call has some advantages. But separate statements for each field is helpful to the user who is juggling several problem descriptions concurrently and would like to cut and paste sections of one into the other.

---

<div class="post-metadata">

### Author: ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)
#### Post date: [December 15, 2024, 1:07am UTC](https://discourse.julialang.org/t/two-structs-with-same-fields-how/123787/8 "2024-12-15T01:07:23Z")

</div>

For completeness, I’ll add that the splatting-discts thing is not unique to `Base.@kwdef`. Any keywords to any function can be set by splatting an `AbstractDict` or `NamedTuple` (and maybe a couple other types beyond those?) after the `;` (which is mandatory for this, if I recall correctly, or at least if no other keywords are provided before it).
