4

I would like to able to get fields from a mutable struct in Julia using a variable.

e.g.

mutable struct myType
    my_field1::Int = 1
    my_field2::Int = 2
    my_field3::Int = 3
end

And then let's imagine you declare a particular instance of this struct using struct_instance = myType()

How can you extract the value of a field from an instance of this mutable struct in a variable fashion?

Let's say you want to assign the value of my_struct.field[X] to a variable, using a for-loop, so that the particular field you're currently accessing depends on the variable X:

foo = zeros(Int64, 3)
for X = 1:3
    foo(X) = struct_instance.field[X]
end

I don't know how to actually implement the above for-loop -- what I wrote above is just pseudo-code above. In MATLAB you would use the following notation, for instance:

foo = zeros(1,3)
for x = 1:3
    foo(x) = struct_instance.(sprintf('field%d',x))
end

Thanks in advance.

Conor
  • 691
  • 5
  • 14
  • 1
    What you want to do in this case is to make a single variable in the struct: `my_field` and make that element be a `Vector{Int}`. – Oscar Smith May 20 '20 at 00:17

2 Answers2

5

The function to get fields from a struct is fieldnames:

julia> mutable struct A
       x
       y
       z
       end

julia> fieldnames(A)
(:x, :y, :z)

To set those field values* programmatically, you can use setproperty!:

julia> a = A(1,1,1)
A(1,1,1)

julia> a.x
1

julia> setproperty!(a, :x, 2)
2

julia> a.x
2

With a for loop,

julia> for i in fieldnames(A)
           setproperty!(a, i, 3)
       end

julia> a
A(3,3,3)
miguel raz
  • 506
  • 2
  • 6
  • 2
    ~It might also be worth mentioning `propertynames` and `getproperty` - with this, you can mimic all the behaviour of fields.~ Also, to work with _fields_ explicitly, you can use `getfield` and `setfield!`, which are generally not overloaded. This allows you access to the fields of the struct, not just the exposed properties. – Anshul Singhvi May 20 '20 at 04:21
  • 1
    This is helpful but not exactly what I was imagining. I imagine the solution involves using `getproperty!()` and passing in a string. What I mean is to use a variable to generate a string(that I know will be a viable field name), that then _itself_ will be used to access the field (or property, I guess?). In MATLAB this is called dynamic fieldnames. It's not that I'm changing _which_ field I access using a variable: the NAME of the field itself is generated using a variable. And then I use that variable field-name (generated as a string, I guess?) to access the field of the struct. – Conor May 20 '20 at 11:30
5

For the code at the beginning of your example to work you need the Paramaters package, otherwise you are not able to have default values (the code from your example throws an error). I use it very often exactly in situations where I need a struct to use to represent a bunch of variables.

using Parameters

@with_kw mutable struct MyType
    my_field1::Int = 1
    my_field2::Int = 2
    my_field3::Int = 3
end

This generates also a set of keyword methods that can be used to set the fields programmatically when creating the object. Have a look at the following code:

julia> vals = [Symbol("my_field$i") =>10i for i in 2:3 ]
2-element Array{Pair{Symbol,Int64},1}:
 :my_field2 => 20
 :my_field3 => 30

julia> MyType(;vals...)
MyType
  my_field1: Int64 1
  my_field2: Int64 20
  my_field3: Int64 30

We have created here a set of field names and now we are using it when creating an object. This approach is particularly useful when you could consider using immutable object instead of mutable ones (immutable objects are always much faster).

You can mutate the object using setfield!:

julia> for i in 1:2
           setfield!(m,Symbol("my_field$i"), 20i)
       end
julia> m
MyType
  my_field1: Int64 20
  my_field2: Int64 40
  my_field3: Int64 30

I think that when you are coming from Matlab this would be the most convenient way to layout your structs.

Przemyslaw Szufel
  • 40,002
  • 3
  • 32
  • 62
  • Thanks! This is exactly what I was looking for - basically, that `Symbol` type that can take a string as input was the critical thing I was missing. Also, with regards to using immutable objects vs. mutable objects: if the fields themselves were arrays that I wanted to populate over time, could I still use an immutable object for that? I used a mutable object because I want to update the values of the fields over time in my application. – Conor May 20 '20 at 19:52
  • 2
    For the record, you can also index fields by number, e.g., `setfield(m, 2, 15)`. – tholy May 20 '20 at 22:29
  • 1
    @user13259528 Yes you can mutate elements of `Array`s that is a part of an immutable `struct` but not the `Array` fields themselves. Julia uses convention pass-by-sharing - have a look at this thread: https://stackoverflow.com/questions/58150295/how-to-pass-an-object-by-reference-and-value-in-julia – Przemyslaw Szufel May 20 '20 at 23:36