1

The following code allows to modify a field of an immutable compsite type, and I am not sure why is this allowed:

struct A
    a :: Vector{Int}
    function A(a :: Vector{Int})
        new(a)
    end
end

myA = A([1,2,3])
@show myA.a # myA.a = [1, 2, 3]
push!(myA.a, 4)
@show myA.a # myA.a = [1, 2, 3, 4]

Instead, modifying it through a type method is not allowed:

struct A
    a :: Vector{Int}
    function A(a :: Vector{Int})
        new(a)
    end
end

function changeAa!(myA::A, a::Vector{Int})
    myA.a = a
end

myA = A([1,2,3])
changeAa!(myA, [1,2,3,4])

Which yields: ERROR: LoadError: setfield!: immutable struct of type A cannot be changed (as it should). Looking at this question, it seems that the object that the field is pointing to needs to be preserved (which is the case for push!), but I am still unsure how I could implement that myself. Can someone please elaborate on this? Thanks.

b-fg
  • 3,959
  • 2
  • 28
  • 44

1 Answers1

1

I am still unsure how I could implement that myself.

It is not clear what you would want to implement. Could you please explain?


myA.a = a is not allowed because it would modify the object that the myA variable is bound to, which is not allowed.

Now note that in the push! variant the following expression:

push!(myA.a, 4)

is equivalent to writing e.g.:

b = myA.a
push!(b, 4)

I am writing this to show you the important thing is that Vector is mutable (as opposed to struct). It does not matter if this object is bound to myA.a or b. The push! function is just passed a value, which is a Vector, and since this vector is mutable it changes it.

In summary: the key thing to understand is that in Julia you work with values. Values are either mutable or immutable. Then you can bind values to variable names. However, to what variable name a value is bound does not influence the properties of this value. Variable name is just a "label" allowing you to reference to the value.

Does this clarify your doubts?


EDIT:

The fact that myA is immutable means that the representation in memory of myA does not change.

And what is the representation in memory of myA? It is only a pointer to the array. What cannot change is the value of the pointer, but the contents of memory that the pointer points to can change.

The function you want can be defined as:

function changeAa!(myA::A, a::Vector{Int})
    copy!(myA.a, a)
end
Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
  • I meant how I can implement a function that modifies a mutable field of an immutable struct. I think my misunderstanding is thinking that `myA.a` was also immutable (same as `A`). I still fail to see the main point though - Modifying the `Vector` shouldn't also mean to modify the `struct`? – b-fg Mar 14 '22 at 13:27
  • So I think that using `myA.a[:] = [1,2,3,4]` inside `changeAa` (after `resize!(myA.a,4)`) is a good way to see this. You can modify the `Vector` entries/length, but you cannot assign a whole new object to the field (such as `myA.a = [1,2,3,4]`). – b-fg Mar 14 '22 at 13:47
  • I have added an explanation (I hope it helps) and an example how you can implement your function. The `copy!` function perform an in-place copy of source vector to a destination vector in your case. – Bogumił Kamiński Mar 14 '22 at 13:53