# Unexpected change of field value in struct

**URL:** <https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364>\
**Category:** General Usage\
**Tags:** parametric-types\
**Created:** [April 22, 2024, 11:15pm UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364 "2024-04-22T23:15:58Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 22, 2024, 11:15pm UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/1 "2024-04-22T23:15:58Z")

</div>

I was not expecting the change in the field values in the following example. Is this a recent change?

```julia
julia> x = [10., 10.]
2-element Vector{Float64}:
 10.0
 10.0

julia> @kwdef struct Test
        a = x
        b = x
       end
Test

julia> T = Test()
Test([10.0, 10.0], [10.0, 10.0])

julia> a = T.a
2-element Vector{Float64}:
 10.0
 10.0

julia> a[1] = 1.
1.0

julia> Test()
Test([1.0, 10.0], [1.0, 10.0])

```

I also find that using `copy(x)` has unexpected behaviour:

```julia
julia> x = [10., 10.]
2-element Vector{Float64}:
 10.0
 10.0

julia> @kwdef struct Test
        a = copy(x)
        b = x
       end
Test

julia> T = Test()
Test([10.0, 10.0], [10.0, 10.0])

julia> a = T.a
2-element Vector{Float64}:
 10.0
 10.0

julia> a[1] = 1.
1.0

julia> Test()
Test([10.0, 10.0], [10.0, 10.0])

julia> b = T.b
2-element Vector{Float64}:
 10.0
 10.0

julia> b[1] = 1.
1.0

julia> Test()
Test([1.0, 10.0], [1.0, 10.0])

julia> x = [10., 10.]
2-element Vector{Float64}:
 10.0
 10.0

julia> Test()
Test([10.0, 10.0], [10.0, 10.0])

```

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [April 23, 2024, 12:02am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/2 "2024-04-23T00:02:39Z")

</div>

It’s always been like that. Writing

```julia-repl
@kwdef struct Test
    a = x
    b = x
end

```

Means `Test` instances initialize both fields with the value `x`.

Since `x` is a _reference_ to an array, then `.a` and `.b` are also references to the same array, so they’re not just equal, but identical:

```julia-repl
julia> t1 = Test()
Test([10.0, 10.0], [10.0, 10.0])

julia> x === t1.a === t1.b
true

```

Hopefully that also explain the `copy`.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [April 23, 2024, 12:09am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/3 "2024-04-23T00:09:04Z")

</div>

Looking at the expansion of `@kwdef` can also provide some insight:

```julia-repl
julia> @macroexpand @kwdef struct Test
           a = copy(x)
           b = x
       end
quote
    #= util.jl:609 =#
    begin
        $(Expr(:meta, :doc))
        struct Test
            #= REPL[25]:2 =#
            a
            #= REPL[25]:3 =#
            b
        end
    end
    #= util.jl:610 =#
    function Test(; a = copy(x), b = x)
        #= REPL[25]:1 =#
        Test(a, b)
    end
end

```

The key is that `copy(x)` and `x` become the default values of a method. So `copy` get called every time the method gets invoked.

---

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 23, 2024, 12:13am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/4 "2024-04-23T00:13:32Z")

</div>

I’m not sure I understand why this is expected. The behaviour is different if x is a scalar:

```julia
julia> y = 1.0
1.0

julia> @kwdef struct TestScalar
        a = y
        b = y
       end
TestScalar

julia> T = TestScalar()
TestScalar(1.0, 1.0)

julia> a = T.a
1.0

julia> a = 2.
2.0

julia> TestScalar()
TestScalar(1.0, 1.0)

julia> 

```

Moreover, from the definition of `copy(x)` it is not a reference:

```julia
copy(x)

  Create a shallow copy of x: the outer structure is copied, but not all internal values. For example, copying an array produces a new array with identically-same elements as the original.

```

An example:

```julia
julia> x = [10., 10.]
2-element Vector{Float64}:
 10.0
 10.0

julia> y = copy(x)
2-element Vector{Float64}:
 10.0
 10.0

julia> z = x
2-element Vector{Float64}:
 10.0
 10.0

julia> x[1] = 2.
2.0

julia> x
2-element Vector{Float64}:
  2.0
 10.0

julia> y
2-element Vector{Float64}:
 10.0
 10.0

julia> z
2-element Vector{Float64}:
  2.0
 10.0

```

The change in `z` is expected.

Edit: I am not changing the original `Array` in the first example, only the value of a new variable that is created from a `struct` field. This `struct` is supposed to be immutable.

---

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 23, 2024, 12:33am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/5 "2024-04-23T00:33:42Z")

</div>

I see that there might be some complications when the default field values are `Arrays` that are defined elsewhere, because this is a macro, but I still consider it unexpected behaviour.

1. It seems to happen only with `Arrays`
2. The `struct` is supposed to be immutable
3. Copying has inconsistent behaviour. Field `a` doesn’t change when an element of variable `a` changes, but it does change when an element of variable `b` changes that is equal to field `b` and not `a`.

---

<div class="post-metadata">

**Author:** ![savq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/savq/32/22063_2.png) [@savq](https://discourse.julialang.org/u/savq)\
**Post date:** [April 23, 2024, 12:46am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/6 "2024-04-23T00:46:31Z")

</div>

> It seems to happen only with `Arrays`

Sort of. This is explained in the [documentation](https://docs.julialang.org/en/v1/manual/arrays/) (though it should be much more prominent)

> In Julia, all arguments to functions are [passed by sharing](https://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_sharing) (i.e. by pointers). Some technical computing languages pass arrays by value, and while this prevents accidental modification by callees of a value in the caller, it makes avoiding unwanted copying of arrays difficult. By convention, a function name ending with a `!` indicates that it will mutate or destroy the value of one or more of its arguments (compare, for example, [`sort`](https://docs.julialang.org/en/v1/base/sort/#Base.sort) and [`sort!`](https://docs.julialang.org/en/v1/base/sort/#Base.sort!)). Callees must make explicit copies to ensure that they don’t modify inputs that they don’t intend to change. Many non-mutating functions are implemented by calling a function of the same name with an added `!` at the end on an explicit copy of the input, and returning that copy.

---

<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:** [April 23, 2024, 12:50am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/7 "2024-04-23T00:50:44Z")

</div>

> [@pmarg](#):
>
> ’m not sure I understand why this is expected. The behaviour is different if x is a scalar:

This is because the scalar is an immutable value. When the binding is to an array, what is immutable is the reference to the array. You cannot bind the field of the struct to _another_ array, but you can change the element of the array. When the field is bound to a scalar, you cannot change the value, period, because the scalar is not a reference to mutable object, it is just the value, which you declared to be immutable by the definition of your struct.

> [@pmarg](#):
>
> It seems to happen only with `Arrays`

It happens with any mutable object:

```julia-repl
julia> mutable struct A
           x
       end

julia> a = A(1.0)
A(1.0)

julia> @kwdef struct B
           x = a
           y = a
       end
B

julia> b = B()
B(A(1.0), A(1.0))

julia> a.x = 2.0
2.0

julia> b
B(A(2.0), A(2.0))

```

---

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 23, 2024, 12:54am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/8 "2024-04-23T00:54:38Z")

</div>

The main issue is not that I modify the initial `x`, which modifies the field values (although I consider this unexpected as well, since the `struct` is immutable).

I create a variable from the field value of `a`, I modify one element and both `a` and `b` change. Even if fields `a` and `b` are references to `Array` x, the variables are references to one of the two fields. I don’t expected the other field to change.

---

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 23, 2024, 12:57am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/9 "2024-04-23T00:57:41Z")

</div>

> [@lmiq](#):
>
> This is because the scalar is an immutable value. When the binding is to an array, what is immutable is the reference to the array.

Thank you, that clarifies the change in the fields when the initial `Array` changes. I just need to take that into account.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 23, 2024, 1:45am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/10 "2024-04-23T01:45:11Z")

</div>

> [@savq](#):
>
> This is explained in the [documentation](https://docs.julialang.org/en/v1/manual/arrays/) (though it should be much more prominent)

It’s the [very first subsection of the “Functions” chapter](https://docs.julialang.org/en/v1/manual/functions/#man-argument-passing).

Basically, this thread seems to be an instance of the common [assignment vs. mutation confusion](https://docs.julialang.org/en/v1/manual/variables/#man-assignment-expressions), since argument passing is effectively just assignment.

---

<div class="post-metadata">

**Author:** ![pmarg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pmarg/32/936_2.png) [@pmarg](https://discourse.julialang.org/u/pmarg)\
**Post date:** [April 23, 2024, 2:25am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/11 "2024-04-23T02:25:04Z")

</div>

To sum up (correct me if I’m wrong):

1. Changing the initial `Array` changes the field values because of the assignment
2. Creating a variable introduces another assignment and `a = T.a = x` which also changes `T.b` since `T.b =x`
3. `copy(x)` doesn’t create an assignment, so changing `a` doesn’t change `T.b` since `a = T.a` and `T.b = x`. However, changing `b` changes both `T.a` and `T.b` since `b = T.b = x` and every time I create a new instance of `Test`, `T.a` copies the new value of `x`.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 23, 2024, 11:40am UTC](https://discourse.julialang.org/t/unexpected-change-of-field-value-in-struct/113364/12 "2024-04-23T11:40:17Z")

</div>

> [@pmarg](#):
>
> Creating a variable introduces another assignment and `a = T.a = x` which also changes `T.b` since `T.b =x`

By itself, `a = T.a = x` doesn’t “change” `T.b`, even though `T.b = x`, it just makes `a` and `T.a` “point” to the same array `x` as `T.b`.
