Making a structure that contains a static array

I’m trying to make a structure that contains a static array. Here’s a minimal example:

using StaticArrays

mutable struct Foo{N<:Integer,T}
    v :: MVector{N,T}
end

function Foo(x::N, y::T) where {N<:Integer,T}
    w = MVector{x, typeof(y)}(undef)
    Foo(w)
end

In the outer constructor, the 2nd argument is only used to determine the type of the entries in the array. This seems like a kluge, and I’d be grateful to hear about a better approach. But this one, inelegant as it is, ought to work and does work for “Vector”. But it fails with “MVector”. I get a method error, yet the signature of the unmatched method looks as though it ought to match the first of the listed candidates. Here’s the error message:

julia> Foo(3, 1.0)
ERROR: MethodError: no method matching Foo(::MArray{Tuple{3},Float64,1,3})
Closest candidates are:
  Foo(::MArray{Tuple{N},T,1,N}) where {N<:Integer, T} at /Users/rogers/src/MatrixCoalescent/src/foo.jl:4
  Foo(::N, ::T) where {N<:Integer, T} at /Users/rogers/src/MatrixCoalescent/src/foo.jl:8
Stacktrace:
 [1] Foo(::Int64, ::Float64) at /Users/rogers/src/MatrixCoalescent/src/foo.jl:9
 [2] top-level scope at REPL[2]:1

This is the problem. N=3 has 3 isa Integer, but 3 <: Integer is an error.

2 Likes

To expand on @mcabbott’s reply: you should not restrict the type of N if you want to do something like this. Generally you could check N in an inner constructor, but here MArray will do it for you:

julia> using StaticArrays

julia> mutable struct Bar{N,T}
           v::MVector{N,T}
       end

julia> Bar(MVector{:bad,Float64}(undef))
ERROR: ArgumentError: Static Array parameter Size must be a tuple of Ints (e.g. `SArray{Tuple{3,3}}` or `SMatrix{3,3}`).
2 Likes

Thank you both. I’ve got the minimal example working now, using an outer constructor, but I’m still unable to scale up to the code I’m actually working on. In the real code, the structure has several fields. With two fields, the outer constructor has the same signature as the default constructor, and so it just calls itself in an endless loop. To avoid that, I rewrote it using an inner constructor…which doesn’t work.

Here’s a minimal example (with just one field) showing the successful outer constructor (for “Foo”) and the unsuccessful inner constructor (for “Bar”). The code fails at the MVector constructor, even though that line is identical in the two versions.

using StaticArrays

mutable struct Foo{N,T}
    v :: MVector{N,T}
end

function Foo(x, y)
    w = MVector{x, typeof(y)}(undef)
    Foo(w)
end

mutable struct Bar{N,T}
    v :: MVector{N,T}

    function Bar(x, y)
        bar = new{typeof(x),typeof(y)}()
        bar.v = MVector{x, typeof(y)}(undef)
        bar
    end
end

Foo works fine:

julia> Foo(2,2.0)
Foo{2,Float64}([2.564503908e-314, 5.0e-324])

Bar doesn’t:

julia> Bar(2,2.0)
ERROR: DimensionMismatch("No precise constructor for MArray{Tuple{Int64},Float64,1,Int64} found. Length of input was 2.")
 Stacktrace:
 [1] MArray{Tuple{Int64},Float64,1,Int64}(::Tuple{Tuple{Tuple{Tuple{Float64,Float64}}}}) at /Users/rogers/.julia/packages/StaticArrays/mlIi1/src/convert.jl:1
 [2] StaticArray at /Users/rogers/.julia/packages/StaticArrays/mlIi1/src/convert.jl:4 [inlined] (repeats 3 times)
 [3] convert at /Users/rogers/.julia/packages/StaticArrays/mlIi1/src/convert.jl:10 [inlined]
 [4] setproperty!(::Bar{Int64,Float64}, ::Symbol, ::MArray{Tuple{2},Float64,1,2}) at ./Base.jl:34
 [5] Bar(::Int64, ::Float64) at /Users/rogers/src/MatrixCoalescent/src/foo.jl:17
 [6] top-level scope at REPL[3]:1

The error message says that it is trying to match “Tuple{Int64}…”. That ought to be “Tuple{2}…”, but I can’t understand why my code is generating this incorrect call.

You’re making a similar mistake as before: typeof(x) should just be x. There may be some other things too, but start with that.

1 Like

Many thanks. That worked.