Outer constructor results in StackOverflowError

I’m trying to add an outer constructor for a parametric type, so that the object is only returned if it passes some tests. Here’s a MRE of what I’m trying to do:

abstract type MyAbstractType end

mutable struct MyType{ST,LT<:AbstractFloat} <: MyAbstractType
    a::Set{ST}
    b::Set{ST}
    c::Set{Tuple{ST,ST,LT}}
end

function MyType{ST,LT<:AbstractFloat}(a::Set{ST}, b::Set{ST}, c::Set{Tuple{ST,ST,LT}})
    if !all(0.0 .<= [x[3] for x in c] .<= 1.0)
        error("NOPE")
    end
    MyType(a, b, c)
end

i = Set([1, 2]) 
j = Set([3, 4])
k1 = Set([(1, 3, 0.2),(2, 4, 0.5)]) # Correct
k2 = Set([(1, 3, 0.2),(2, 4, 1.4)]) # Incorrect

MyType(i, j, k2) # Working - returns an error
MyType(i, j, k1) # Not working - StackOverflow

This results in a StackOverflowError, because the constructor calls itself (unless there is an error because the conditions are not satisfied).

I’m quite frankly lost about what is wrong with this code. Any tips?

Use an inner constructor:

mutable struct MyType{ST,LT<:AbstractFloat} <: MyAbstractType
    a::Set{ST}
    b::Set{ST}
    c::Set{Tuple{ST,ST,LT}}
    function MyType{ST,LT<:AbstractFloat}(a::Set{ST}, b::Set{ST}, c::Set{Tuple{ST,ST,LT}})
        if !all(0.0 .<= [x[3] for x in c] .<= 1.0)
            error("NOPE")
        end
        new{ST, LT}(a, b, c)
    end
end

Instead do MyType{eltype(a),...}(a, b, c)

thanks!

If you want your struct to be created only if it pass the test you should use an inner constructor, as at-kristoffer.carlsson suggested. This way the test can never be by-passed. The outer constructor test can be by-passed if you call MyType{eltype(a),...}(a,b,c) directly.