Saving data into array having struct data type

Hi, newbie to Julia.

I am trying to run the following

> struct Bus
> bus_num::Int64
> bus_type::Int64
> bus_area::Int64
> bus_vmag::Float64
> bus_vangle::Float64
> bus_vnom::Float64
> bus_vmax::Float64
> bus_vmin::Float64
> end
> 
> aBuses = Array{Bus}(undef,10)
> fields  = ["Bus_N","Bus_Type","Area","V_Mag (pu)","V_Ang (deg)","V_Nom (kV)","Zone","V_max (pu)","V_min (pu)"];
> header = "Area";
> buses = aBuses[5,1]
> Dbus = 0
> # for i in 1:size(fields,1)
> idx = findall(x->x=="Area",fields)
> buses.bus_area = idx[1,1]
> # Dbus[:,i] = data[:,idx]
> # end

However, when I reach the last line

buses.bus_area = idx[1,1]

I received an error that

LoadError: setfield! immutable struct of type Bus cannot be changed

While reading the Julia manual, I understood that once an immutable struct has been defined, it is not possible to change its fields but it is still possible to change the value of a field. Apparently, it seems that this is not the case or I have not understood it right.
Since i am working on the modelling of an electrical network, my idea is to save the data of each bus, line, etc in a struct container (defined for each bus, line etc) and then store all these containers in one array i.e., Array_Bus = [bus_1_container, bus_2_container, and so on], where bus_1_container is the struct Bus that I have already defined in the code. However, I am unable to save the data for each individual field of the struct Bus due to the error that I have mentioned.

If I declare the struct Bus as mutuable, then entries of

aBuses = Array{Bus}(undef,10)

aBuses become undefined and I no longer can access them while executing the command

buses = aBuses[5,1]

Can someone shed some light what is the problem and how can I solve it?

You did not understand right. You cannot change the value of a field in an instance of an immutable struct. You can change the value of fields of an instance of a mutable struct stored inside a immutable object.

julia> mutable struct Mutable; a :: Int; b :: Float64; end

julia> struct Immutable; my_int :: Int; my_mutable_struct :: Mutable; end

julia> i = Immutable(10, Mutable(20, 30.0))
Immutable(10, Mutable(20, 30.0))

julia> i.my_int = 100
ERROR: setfield! immutable struct of type Immutable cannot be changed
...

julia> i.my_mutable_struct = Mutable(200, 300.0)
ERROR: setfield! immutable struct of type Immutable cannot be changed
...

julia> i.my_mutable_struct.a = 200
200

julia> i
Immutable(10, Mutable(200, 30.0))

For mutable or immutable structs it is impossible to change the existing fields after defining the type. If you want this you need to use a Dict or something like that. For your case (where you just wanna change values) I think a mutable struct will suffice.

Thanks for the clarification. As I said in my original post that when I used the mutable Struct type and initialize the

aBuses = Array{Bus}(undef,10)

then, I got this output


Vector{Bus} with 10 elements
#undef
#undef
#undef
#undef
#undef
#undef
#undef
#undef
#undef
#undef

and now, I can no longer access the field of abuses through

aBuses.bus_num = 10

So, with immutable struct, I cannot modify the values of a field and if i declare a mutable struct, and then initialize an array, there are no longer any entries of type mutable struct in the resulting array. Is there any specific reason, why there are no entries of type mutable struct when I initialize an Array?

aBuses is an empty array of buses. #undef is telling you there’s nothing in that entry. You should be able to do:

aBuses[1] = Bus(1,2,3,4,5,6,7,8)

And then, since Bus is mutable, you could mutate it later:

aBuses[1].bus_num = 10
2 Likes

I think one source of confusion is that there seems to be a difference in behaviour of undef during array construction for a struct compared to a mutable struct. Consider:

julia> struct Bus 
           bus_num::Int 
       end

julia> aBuses = Array{Bus}(undef, 2)
2-element Array{Bus,1}:
 Bus(140449318305968)
 Bus(140449315843744)

julia> mutable struct 
           Bus2 bus_num::Int 
       end

julia> aBuses2 = Array{Bus2}(undef, 2)
2-element Array{Bus2,1}:
 #undef
 #undef

so in both cases you get an Array{Bus, 1} back, however with a struct there are Bus objects in the array (with whatever happened to be at that memory location previously reinterpreted as an Int), while with the mutable struct you get #undef fields.

As @tomerarnon points out that just means that you need to populate your array with Bus objects after creation if you want to manipulate the fields of those objects.

1 Like

To complement what @tomerarnon said.

  1. aBuses s not an empty array of buses (isempty will return false), but a vector of length 10 of buses, they just happen to be non-initialized (i.e., they are like a NULL pointer to that object in C/C++). They count as value of that type (you can see the vector only allow Bus objects) but you cannot do anything with them (they do not really exist), you need to replace them with initialized objects.
  2. You can either:
    1. Create the Vector with the correct length but with uninitialized buses (like you are are doing) and then after loop through the vector assigning initialized bus objects to the positions.
    2. Create an empty Vector and then call push! repeatedly, filling it with newly create bus objects at each call.
    3. Create the vector with the right length and initialized objects by using a list comprehension aBuses = [ Bus(i, common_type, other_info[i]...) for i in 1:10 ].
    4. DO NOT: fill(bus_object, 10) this will create a vector of length 10 in which EVERY ELEMENT REFER TO THE SAME OBJECT. Is often a rookie mistake, but even people more experienced with Julia (like me) will forget that one time or another and end up with curious bugs.
1 Like