# Unexpected result with mutable structs

**URL:** <https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457>\
**Category:** New to Julia\
**Tags:** question, struct\
**Created:** [January 24, 2023, 12:50pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457 "2023-01-24T12:50:27Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![SpinolaHours](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@SpinolaHours](https://discourse.julialang.org/u/SpinolaHours)\
**Post date:** [January 24, 2023, 12:50pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/1 "2023-01-24T12:50:27Z")

</div>

Hi there,

I have a mutable struct defined as follows:

```julia
mutable struct Test
    a::Float64
    b::Float64
    c::Float64
end

Test(a,b) = Test(a,b,a+b)

```

I had expected when I update either of fields a or b that c would also be re-calculated. This doesn’t appear to be the case. I.e.  
eg = Test(1,2)  
eg.a = 2

In this case I would expect eg.c = 4 but it is still = 3.

Is there a way to force the recalculation? Or a better way to do this in general?

One solution would be to re-create a non-mutable struct every time instead of updating the values. Is this a reasonable solution?

Thanks for any help.

---

<div class="post-metadata">

**Author:** ![hexaeder](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hexaeder/32/24403_2.png) [@hexaeder](https://discourse.julialang.org/u/hexaeder)\
**Post date:** [January 24, 2023, 1:04pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/2 "2023-01-24T13:04:05Z")

</div>

One way to achieve that is creating “virtual” properties, which are not actually stored inside the struct by overloading the `getproproperty` function.

```julia
mutable struct MyStruct
    a::Float64
    b::Float64
end

function Base.getproperty(s::MyStruct, f::Symbol)
    if f===:c
        return s.a + s.b
    else
        return getfield(s, f)
    end
end

s = MyStruct(1,2)
s.a # 1
s.b # 2
s.c # 3

s.a = 4
s.c # 6

```

This has the drawback, that you’re not memoizing the results but recalculate `c` whenever you need it. Another similar option would be to overload `Base.setproperty!` in a way, that it updates `c` whenever someone changes field `a` or `b`.

```julia
mutable struct MyStruct2
    a::Float64
    b::Float64
    c::Float64
end
MyStruct2(a,b) = MyStruct2(a,b,a+b)

function Base.setproperty!(s::MyStruct2, name::Symbol, x)
    setfield!(s, name, x)
    setfield!(s, :c, s.a + s.b)
end

s = MyStruct2(1,2)
s.a = 4.0
s.c # 6

```

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [January 24, 2023, 1:33pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/3 "2023-01-24T13:33:05Z")

</div>

> [@SpinolaHours](#):
>
> I had expected when I update either of fields a or b that c would also be re-calculated.

The calculation of `a+b` is just something that happens when the constructor, `Test`, is called. And the constructor is _not_ called when you update a field, since there is no construction of a new object going on. The definition of the constructor does not create some sort of ‘link’ between the fields `a`, `b` and `c`.

The function that is called when you modify a field is `setproperty!`, and you will have to work with that function to achieve something like what you want, as shown by @hexaeder.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 24, 2023, 2:33pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/4 "2023-01-24T14:33:49Z")

</div>

The solution above is very interesting, but I think it is much more common just to write a function as:

```julia
julia> function update!(t::Test;a=t.a,b=t.b)
           t.a = a
           t.b = b
           t.c = a + b
           return t
       end
update! (generic function with 1 method)

julia> update!(t;a=2)
Test(2.0, 2.0, 4.0)

```

---

<div class="post-metadata">

**Author:** ![filchristou](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/filchristou/32/26760_2.png) [@filchristou](https://discourse.julialang.org/u/filchristou)\
**Post date:** [January 24, 2023, 2:39pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/5 "2023-01-24T14:39:17Z")

</div>

If you are open to external dependencies with [Observables.jl](https://github.com/JuliaGizmos/Observables.jl) you can do exactly this just with some extra syntax:

```julia
using Observables

struct Test
    a::Observable{Float64}
    b::Observable{Float64}
    c::Observable{Float64}
    function Test(a::T,b::T) where T<:Real
        ao = Observable{Float64}(a)
        bo = Observable{Float64}(b)
        co = Observables.@map &ao + &bo;
        new(ao,bo,co)
    end
end

```

Now do

```julia
julia> eg = Test(1,2)
Test(Observable(1.0), Observable(2.0), Observable(3.0))

julia> eg.a[] = 2
2

julia> eg.c[] #and you get what you want
4.0

```

notice the extra `[]` bracket syntax.

---

<div class="post-metadata">

**Author:** ![StefanKarpinski](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stefankarpinski/32/24_2.png) [@StefanKarpinski](https://discourse.julialang.org/u/StefanKarpinski)\
**Post date:** [January 24, 2023, 4:01pm UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/6 "2023-01-24T16:01:01Z")

</div>

That’s some heavy (but very cool) machinery for this.

---

<div class="post-metadata">

**Author:** ![SpinolaHours](https://avatars.discourse-cdn.com/v4/letter/s/a3d4f5/32.png) [@SpinolaHours](https://discourse.julialang.org/u/SpinolaHours)\
**Post date:** [January 25, 2023, 8:53am UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/7 "2023-01-25T08:53:02Z")

</div>

Thanks. This seems like the neatest approach (although maybe just re-calling the constructor is easier still).

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [January 25, 2023, 11:36am UTC](https://discourse.julialang.org/t/unexpected-result-with-mutable-structs/93457/8 "2023-01-25T11:36:46Z")

</div>

> [@SpinolaHours](#):
>
> maybe just re-calling the constructor is easier

Just be careful because then you are not mutating the object, just creating a new one. Actually this might be the most performant way, if you make the object to be immutable.
