Static Array type syntax

If we look at the documentation for StaticArrays we see this:


Construct a statically-sized array of dimensions S (expressed as a Tuple{...} ) using the data from a . The S parameter is mandatory since the size of a is unknown to the compiler (the element type may optionally also be specified).

That seems pretty straightforward, yet when I try to use this it fails:

julia> m = zeros(3,3,3)
# output omitted
julia> SArray{size(m)}(m)
ERROR: TypeError: in SArray, in S, expected S<:Tuple, got Tuple{Int64,Int64,Int64}
 [1] top-level scope at none:0

julia> isa(size(m), Tuple)

Julia seems mad at me for not giving it a tuple conveying the size as the manual instructs, however we can plainly see the size I pass in is a tuple. Where is my logic breaking down?

size(m) is a runtime call to a function. The compiler must know the size at compile time, ie, you have to enter it as a literal

1 Like

It should be a tuple type, I think e.g.

julia> Tuple{3,3,3}

At least that is what the error message is saying.

1 Like

The use is

julia> using StaticArrays
julia> m = zeros(3,3,3);
julia> s=SArray{Tuple{size(m)...}}(m)
3×3×3 SArray{Tuple{3,3,3},Float64,3,27}:

However, this is pretty slow and not for use in loops:

julia> using BenchmarkTools
julia> g(m)=SArray{Tuple{size(m)...}}(m);
julia> @btime g($m);
  1.998 μs (15 allocations: 1.09 KiB)
julia> @btime SArray{$(Tuple{size(m)...})}($m);
  6.554 ns (0 allocations: 0 bytes)

It is however a valid and very julian optimization to construct the type describing the desired size outside of a long computation (via e.g. sz = Tuple{size(m)...}), and then have a function boundary into the long computation (e.g. expensive_stuff(m, other_args, ::Type{sz}) where sz). This way your long and expensive computation gets compiled for every size you pass in.

If you know the sizes at coding-time (e.g. you know that you want 3x3x3 tensors because 3d elasticity, or something) then you can of course just plug them in.

(actually, I think 2us is pretty fast for what we ask julia to do here)

1 Like

It may be better to use the @SArray macro instead. You don’t have to allocate a normal array first.

julia> @SArray zeros(3,3,3)
3×3×3 SArray{Tuple{3,3,3},Float64,3,27}:
[:, :, 1] =
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0

[:, :, 2] =
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0

[:, :, 3] =
 0.0  0.0  0.0
 0.0  0.0  0.0
 0.0  0.0  0.0

Ok I did not expect to learn as many things as I did from this post, cool, guys. I think the biggest one was the difference between Tuple{3,3,3} and (3,3,3)… understanding that was enough to unstick me, thanks all.


So what is the difference? The second expression is also a tuple. Don’'t get it. :thinking:

The first is a type, the second is a value:

julia> typeof(Tuple{3,3,3})

julia> typeof((3,3,3))

Also, the first one is a pretty interesting parametric type: there could be no value x::Tuple{3,3,3}, but the language does not check type parameters so it’s OK, just like

struct Foo{T}


Ok, but what is the difference between

Tuple{Int64,Int64,Int64} ?

One of the tuple types contain 3 and the other Int. I feel this is a trick question?

1 Like


julia> (3,3,3) isa Tuple{Int,Int,Int}

julia> (3,3,3) isa Tuple{3,3,3}

indeed there exists no value which isa Tuple{3,3,3}.

But why is the expression Tuple{3,3,3} is then used in the following context, if no variable of this type can exist?

Same reason as you can have a type

struct Foo{N} end

and create a Foo{2} even though there is no variable that can have the type 2.

1 Like

The compiler gets to see the types, but not the values of variables. So this is in a sense just a trick to pass more information to the compiler.

For a normal Array{Int64,N} all it knows is the element type and the number of dimensions; the point of StaticArrays is that it also knows there will be exactly 3x3x3 elements here, allowing optimisations which make sense for 3 but not for a million.