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:
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.