# Calling a function to change a struct?

I am trying to use a function in order to modify a struct that is passed to it as a variable. Below is a minimal bit of code I have put together to illustrate my confusion.

``````struct a
x::Int
y::Int
end

function switchVariables!(b::a)
newB = a(b.y, b.x)
b = newB
println(b)
end

b = a(1, 2)
println(b)
switchVariables!(b)
println(b)
``````

The two `println` calls outside of the `switchVariables!` function return `a(1, 2)`, whereas the one inside the function returns `a(2, 1)`. So it looks like the proper operation is being done to `b` inside the function, but not outside of it. Iâ€™ve poked around with variants like `b = deepcopy(newB)` but have not had any luck. Would someone be able to explain to me what is going on here? I recognize that for this minimal example, it would probably be better to just return the desired modified struct rather than explicitly trying to modify `b` - while the same might be true for the actual code that Iâ€™m working on, I would at least like to understand why the above code doesnâ€™t work the way I would naively expect it to. Thanks!

Type

``````?Ref
``````

at the Julia prompt.

1. The struct created is immutable, you cannot modify it.

2. Doing `b = new struct`, inside the function, is just assigning the label `b` to a new struct.

3. That label is local to the scope of the function.

You need to use `b = switch(b)` to assign the label `b` of the outer scope to the new struct created in the function. But that is not mutating anything. Usually thatâ€™s fine and the way to go, but you can also use a mutable struct and get actually the behavior you were expecting.

Ps. Think of an immutable struct as a number. You are doing:

``````function switch(b)
newb = 2
b = newb
return b
end

b = 1
switch(b)
``````

You will see that `b` does not change outside `switch`, because the label `b` of the outer scope was bound to the value `1` and that was not changed in that scope. You need to do here `b = swich(b)`. The same goes for immutable objects, like your struct. In any case, you are not modifying the immutable number 1, which is an immutable value, corresponding to your struct.

7 Likes

To get the behaviour you ask for, you should do this:

``````mutable struct a
x::Int
y::Int
end

function switchVariables!(b::a)
b.x, b.y = b.y, b.x
end
``````

Unless you declare the struct as `mutable` it will be immutable, i.e. the content canâ€™t be modified. This has a number of advantages for performance. But if you want modifiable struct you must explicitly use the `mutable` keyword. The way you wrote your function would have worked if arguments were â€ścalled by nameâ€ť, Julia does not support that.

4 Likes
``````global b = newB
``````

If you can get `@set` to turn iteration-based destructuring assignment `b.x, b.y = b.y, b.x` to instantiation `a(b.y, b.x)` then please provide the code because AFAIK Accessors.jl canâ€™t do that yet. Itâ€™s not possible to do the changes separately because both the previous x and y fields must be retrieved before replacing either.

``````julia> @set b.x = b.y
a(2, 2)

julia> @set b.x, b.y = b.y, b.x
(2, 1)
``````

EDIT: Answered my own question, but tbf, it is a longstanding missing feature in Accessors.jl and the predecessor Setfield.jl. `setproperties` is only mentioned in the docs under an explicitly experimental `mapproperties`, so it could easily be gone in minor revisions. I donâ€™t know the internal workings of why `@set` canâ€™t just do what `setproperties` can.

``````julia> b
a(1, 2)

julia> setproperties(b, (x = b.y, y = b.x))
a(2, 1)
``````

Note also that Accessors, or Setfield, do not mutate immutable data, really, they are just convenience macros to redefine new immutable structs. The issue the OP has would be the same with it:

``````
julia> using Accessors

julia> struct A
i
j
end

julia> function set_i(a)
a = @set a.i = 0
return a
end
set_i (generic function with 1 method)

julia> b = A(1,1)
A(1, 1)

julia> set_i(b)
A(0, 1)

julia> b # doesnÂ´t  change
A(1, 1)
``````
1 Like

Yeah youâ€™re absolutely right!