It’s not needed, but the types T1,T2,T3 can’t be infered automatically from the Vector types you are using here. Without the outer constructor you get:
julia> Cluster(p,r,m,s,"type")
ERROR: MethodError: no method matching Cluster(::Vector{SVector{3, Float64}}, ::Vector{Vector{Float64}}, ::Vector{String}, ::Vector{SVector{3, Int64}}, ::String)
For this to work without the outer constructor you have to give the types T1,T2,T3 explicitly:
julia> Cluster{Float64,Float64,Int64}(p,r,m,s,"type")
Let’s change to a more minimal example:
julia> struct Example1{T1}
v::T1
function Example1(v::T1) where T1
new{T1}(v)
end
end
julia> Example1(2)
Example1{Int64}(2)
julia> struct Example2{T1}
v::Vector{T1}
function Example2{T1}(v) where T1
new(v)
end
end
julia> Example2([2,3])
ERROR: MethodError: no method matching Example2(::Vector{Int64})
Stacktrace:
[1] top-level scope
@ REPL[13]:1
julia> Example2{Int64}([2, 3])
Example2{Int64}([2, 3])
For example2 an outer constructor would be convenient:
julia> Example2(v::Vector{T1}) where T1 = Example2{T1}(v)
Example2
julia> Example2([2,3])
Example2{Int64}([2, 3])
This would be a way to do it only with an inner constructor:
julia> struct Example3{T1}
v::Vector{T1}
function Example3(v::Vector{T1}) where T1
new{T1}(v)
end
end
julia> Example3([2,3])
Example3{Int64}([2, 3])
Now the new{T1}
is needed, as in Example1, because it isn’t specified at the definition of the constructor. If it is specified in the constructor, like in Example2, the calling must be the same, as in Example2{Int64}([2, 3])
.
Now in your case, this would look like:
using StaticArrays
struct Cluster{T1,T2,T3}
positions::Vector{SVector{3,T1}}
rotations::Vector{Vector{T2}}
materials::Vector{String}
sizes::Vector{SVector{3,T3}}
type::String
Cluster(
p::Vector{SVector{3,T1}},
r::Vector{Vector{T2}},
m::Vector{String},
s::Vector{SVector{3,T3}},
t::String
) where {T1,T2,T3} = (length(p) == length(r) == length(s) == length(m)) ? new{T1,T2,T3}(p,r,m,s,t) : error("length mismatch")
end
N = 3
p = [@SVector rand(3) for _ in 1:N]
r = r=[ rand(Float64,3) for _ in 1:N ]
s = [SVector(1,2,3) for _ in 1:N]
m = ["Au" for _ in 1:N]
Cluster(p,r,m,s,"type")
r2 = r=[ rand(Float64,3) for _ in 1:2 ]
Cluster(p,r2,m,s,"type")
I have tried a few other combinations of e.g. Example{T1}(...)
and new{T1,...}
, but above two possible ways are the only ones which work. Well, I admit, I can’t really explain, why it is, as it is, but my guess is, at some point a specific syntax must be fixed, even if there are other logical ways to express explicitly what you want (from the point of the developer), or in other words, not every possible expression must be made valid.