I am having problems declaring a parametric structure with @kwdef
and an inner constructor to perform some checks.
My struct has many parameters and complicated checks, but this simple example is enough to explain my problems:
@kwdef struct B{T <: Number}
x::T = zero(T)
B(x) = begin
(x < zero(x)) && throw(DomainError("No negative values!"))
new{typeof(x)}(x)
end
end
This works for all the case of interest but the last one:
julia> B(0)
B{Int64}(0)
julia> B(0.0)
B{Float64}(0.0)
julia> B(x = 0)
B{Int64}(0)
julia> B(x = 0.0)
B{Float64}(0.0)
julia> B{Int}()
ERROR: MethodError: no method matching B{Int64}(::Int64)
Closest candidates are:
B{T}(; x) where T<:Number
@ Main util.jl:579
Stacktrace:
[1] B{Int64}(; x::Int64)
@ Main ./util.jl:579
[2] B{Int64}()
@ Main ./util.jl:579
[3] top-level scope
@ REPL[50]:1
I tried to explore what @kwdef
is doing using @macroexpand
, but I find nothing strange: in the definition of B
I see one inner constructor (the one I defined) and two outer constructors defined by @kwdef
, including B{T}(; x = zero(T)) where T <: Number
: I cannot understand why this is not called by B{Int}()
.
May somebody explain me why B{Int}()
is incorrect, please? What would be the best way to construct B
without specifying any parameter but the type T
?