Modify elements of struct without changing original inputs

Hi,

I have a question regarding a situation where I want to pass a (mutable) struct to another struct.
Please see the MWE below.

mutable struct MyStruct
    arr::Array{Real, 1}
end

struct MyOtherStruct
    ms::MyStruct
end

function square_MyOtherStruct(mos::MyOtherStruct)
    mos.ms.arr = mos.ms.arr.^2
end


my_struct = MyStruct([1, 2, 3])
my_other_struct = MyOtherStruct(my_struct)

square_MyOtherStruct(my_other_struct)

This results in:

println(my_other_struct.ms.arr)
 [1,4,9]
println(my_struct.arr)
 [1,4,9]

What I would like to achieve is to modify the ms::MyStruct element within the MyOtherStruct without changing the mystruct variable that is passed to create the MyOtherStruct.

So I would like to (still) have:

println(my_other_struct.ms.arr)
 [1,4,9]
println(my_struct.arr)
 [1,2,3]

In my actual application, I want the user to still have “original” my_struct object at hand to proceed using this, while also able to perform other operations on the MyOtherStruct object that might include modifications of the contained values.

I know that this is possible by using a constructor for MyOtherStruct that makes a deepcopy of the MyStruct object before construction.

But I was wondering whether this is a recommended way of doing this or if there is a cleaner solution, especially since I read that using deepcopy is usually not a good thing and might also have some negative effects on the performance.

Either make a copy or use a non-mutable struct. Having changes visible to other references is the whole point of a mutable struct.

Even if you use a non-mutable struct, if it has a mutable member like an Array then you have to make a copy of that if you don’t want changes visible to other places that refer to the same Array. For example, if you did

mos.ms.arr .= mos.ms.arr.^2

(note the .=) which mutates mos.ms.arr in place rather than making a new array and re-assigning mos.ms.arr (as in your square_MyOtherStruct code above), then even if ms is non-mutable the change to arr will be visible to other places that refer to arr.

(Note also that your abstract element type in arr::Array{Real, 1} will kill performance. It’s better to use a parameterized struct type so that you can use a concretely-typed Array here.)

2 Likes

There are some packages which provide a convenient API to make a copy with one field modified, eg

1 Like