Array of Mutable structs containing static arrays


I’m a new user coming from Python and C.

I would like to:

  1. create a struct with arrays of known length as attribute.
  2. Be able to update these arrays after the creation
  3. Be able to allocate a multitude of these structs contiguously in memory
  4. Copy this block to a CUDA device

I tried multiple packages like StaticArrays and StructArrays but I can’t seem to find a solution that satisfies all these constraints.

A SVector+StaticArray generates the struct I want but I’m not able to update the arrays.

To make it clearer for people with experience in python/numpy/c. This is what I want to achieve:

np.empty(5, dtype=np.dtype([
('field', (np.int32, 5)

or in C

struct test {
 int field[5];
calloc(5, sizeof(struct test));

Thank you for your help

The rough equivalent in Julia is:

struct Test
a = fill(Test(@SVector Cint[0,0,0,0,0]), 5)

This is stored contiguously in memory. You can change it with e.g.

a[3] = Test(@SVector Cint[1,3,5,7,9])

A more convenient way to mutate individual fields (without replacing the whole struct) is provided by e.g. Setfield.jl.


Thanks for the answer!

Just out of curiosity. Why is there a need for an external library to perform such a basic operation?

Oh my assumptions was that Setfield.jl was actually doing some unsafe casting to overcome the immutability of the struct. It’s just a syntactic sugar over doing a reassignment. I guess I just have to think of julia as a functional programming language and trust the compiler to do a good job generating efficient code :slight_smile:

1 Like

The Julia equivalent of a Numpy array is a (typed) standard Julia array. Both have variable length.
In this case, using a SVector from StaticArrays.jl improves performance because it allows additional optimizations (like avoiding heap allocations).

Edit: I just saw that you are using a custom dtype consisting of 5 integers, therefore the optimizations above are probably also done in Numpy.

Please correct me if I’m wrong, but isn’t a simple vector of immutable structs also a solution?

struct Test{T}
   i :: T
   j :: T
   k :: T
vec = Vector{Test{Int}}(undef,5)
julia> vec[1] = Test(1,1,1)
Test{Int64}(1, 1, 1)

I mean, isn’t this essentially equivalent to a vector of SVectors, just without the convenience of those?

I don’t think it is. I need an array of variable amount of structs that have constant (actually parametric but I was simplifying here) number of fields.

(I would like the forum to have an option to fork the threads to ask stupid questions without polluting the OP, so sorry in advance).

Actually my question was related to the fact that the solution provided,

struct Test
  field :: SVector{5,Int}
a = fill(Test(@SVector Int[0,0,0,0,0]), 5)

seems somewhat redundant, since one can write directly

a = fill((SVector{5,Int}(0,0,0,0,0), 5)

and get a vector of constant-size vectors (at least in the minimal example I don’t see the need of the Test struct here).

Than you asked why one needs an external library, StaticArrays for that, and I was pointing that if the number of fields is known in advance, you don’t, you can use a vector of immutable structs. Of course, StaticArrays provides the convenience of the parametric number of fields and, furthermore, the implementation of many arithmetic operations, zeros, etc.

Also I think a vector of tuples can do the same:

julia> v = [ (0,0) for i in 1:1_000_000 ];

julia> v[1]
(0, 0)

julia> function mutate_vec!(v)
         for i in 1:length(v)
           v[i] = (1,1)
mutate_vec! (generic function with 1 method)

julia> @allocated mutate_vec!(v) # compiles

julia> @allocated mutate_vec!(v)

julia> v[1]
(1, 1)

As far as I understand (which is not much), that is contiguous in memory.

My understanding was that in the actual application the struct would have other fields as well.


@stevengj You are absolutely right, it’s just that I was trying to abstract my question as much as possible.

Thank you both for all your very helpful answers!