首页 > 解决方案 > Julia: How to use the @assert macro with a struct? @with_kw vs Base.@kwdef

问题描述

I want to use the @assert macro with a struct. The @with_kw macro from package Parameters can be used with no explicit method built on top of the struct, whereas Base.@kwdef needs an explicit method. Did I understand this correctly?

# 1. outer constructor with "semi-colon" keyword + no keyword macro + @assert
struct myStruct1
    a :: Float64
end
function myStruct1(;a::Float64=1.0)
    @assert a > 0 "must be positive"
    myStruct1(a)
end
myStruct1().a
## 1.0 
myStruct1(a = -1.0).a
## ERROR: LoadError: AssertionError: must be positive

# 2.1. Base.@kwdef + no outer constructor + @assert
Base.@kwdef struct myStruct2
    a :: Float64 = 1.0
    @assert a > 0 "must be positive"  # was hoping this would work
end 
myStruct2().a
## ERROR: LoadError: UndefVarError: a not defined

# 2.2. Base.@kwdef + outer constructor + @assert
Base.@kwdef struct myStruct22
    a :: Float64 = 1.0
end 
function myStruct22(;a::Float64=1.0)  # explicit method just to @assert 
    @assert a > 0 "must be positive"
    myStruct22(a)
end
myStruct22().a
## 1.0
myStruct22(a = -1.0).a
## ERROR: LoadError: AssertionError: must be positive

# 2.3. @with_kw + outer constructor + @assert
using Parameters
@with_kw struct myStruct23
    a :: Float64 = 1.0
    @assert a > 0 "must be positive"  # works without explicit method
end 
myStruct23().a
## 1.0
myStruct23(a = -1.0).a
# ERROR: LoadError: AssertionError: must be positive

标签: julia

解决方案


Currently, Base.@kwdefdoes not support that, since, as you said, it uses an outer constructor. You can check the generated code with @macroexpand macro:

julia> @macroexpand Base.@kwdef struct myStruct2
           a :: Float64 = 1.0
           @assert a > 0 "must be positive"  # was hoping this would work
       end
quote
    #= util.jl:472 =#
    begin
        $(Expr(:meta, :doc))
        struct myStruct2
            #= REPL[3]:2 =#
            a::Float64
            #= REPL[3]:3 =#
            if a > 0
                nothing
            else
                Base.throw(Base.AssertionError("must be positive"))
            end
        end
    end
    #= util.jl:473 =#
    myStruct2(; a = 1.0) = begin
            #= util.jl:450 =#
            myStruct2(a)
        end
end

As you see, the assert code in the struct definition but not in the outer constructor.


推荐阅读