Struct constructors with partial types

Suppose I make a struct as follows

struct MyStruct{A,B}
       x::A
       y::B
end

Why is it that the following two constructions work:

julia> MyStruct(3,4)
MyStruct{Int64, Int64}(3, 4)
julia> MyStruct{Int64, Int64}(3,4)
MyStruct{Int64, Int64}(3, 4)

but this one fails?

julia> MyStruct{Int64}(3,4)
ERROR: MethodError: no method matching (MyStruct{Int64, B} where B)(::Int64, ::Int64)

Intuitively, B should just match Int64 in MyStruct{Int64, B} where B and things should work, just like both types get matched in MyStruct(3,4), which is equivalent to (MyStruct{A,B} where {A,B})(3,4)? But I must be misunderstanding something because that is not happening, I would greatly appreciate some clarity!

1 Like

From the docs about composite types:

Two constructors are generated automatically (these are called default constructors ). One accepts any arguments and calls convert to convert them to the types of the fields, and the other accepts arguments that match the field types exactly.

These two default constructors are MyStruct(x,y) and MyStruct{A,B}(x,y).

You can add your missing constructor by defining it afterwards:

struct MyStruct{A,B}
       x::A
       y::B
end
MyStruct{A}(x, y) where A = MyStruct(convert(A, x), y)

Alternatively, you can opt-out of having the two default constructors by defining inner constructors.

struct MyStruct{A,B}
       x::A
       y::B
       MyStruct{A}(x, y) where A = MyStruct(convert(A, x), y)
end

Now, there is only the one constructor.

2 Likes

That makes sense. But I guess Iā€™m still a little confused by the error message. Taken literally, there is a method matching MyStruct{Int64, B} where B, which is MyStruct{Int64, Int64}, so what does that error message mean?

1 Like

How does that match? The first has an unbound type variable, which could be any type, while the latter requires that second type to be Int.

1 Like