# Weird behavior of for loop+copy syntax

Dear All
Here is a minimum working example:

``````function test_func()
A=[zeros(Float64,2) for _ in 1:2]
B=copy(A)
for ja in 1:2, jb in 1:2
(B[ja][jb],A[ja][jb])=(1.0,2.0)

end
print(B)
print(A)
end
``````

When I call this function, it prints out two matrix, with 2.0 on all its entries

Why does Julia behave in this way? When I write B=copy(A), shouldn’t they refer to different object rather than the same reference? Then when I try to assign all the entries of B to be 1, and all the entries of A to be 2, why doesn’t this grammar work?

Welcome to the trickiness of copying. In Julia (and many other languages), `copy` is a shallow copy that only duplicates the outside structure.

Here `A` is a `Vector{Vector{Float64}}` and so only the outermost `Vector` is copied, the internal `Vector{Float64}`s are not. So both `A` and `B` end up with the same contents and you get the behaviour you are experiencing.

What is confusing is that types like `Float64` aren’t references so if you `copy` a `Vector{Float64}` you do end up with distinct vectors. This can be shown with a simple example:

``````A = [1, 2]
B = copy(A)
A[1] = 2
B[1] == 1 # true
``````
2 Likes

works with

`````` @doc deepcopy
``````
1 Like

It’s easier and consistent to think of a shallow copy of a mutable data structure as a copy sharing elements/instances with the original.

``````julia> x = [1];

julia> x2 = copy(x);

julia> x[begin] === x2[begin]
true

julia> y = [[1]];

julia> y2 = copy(y);

julia> y[begin] === y2[begin]
true
``````

The elements of `y` and `y2` both contain the same instance `[1]`, so mutating that instance by reassigning its element will be reflected in both `y` and `y2`. This doesn’t apply to `x`’s immutable elements.

``````julia> y[begin][begin] = 2; # mutates y[begin], not y

julia> y, y2
([[2]], [[2]])
``````

You can mutate `x` by reassigning its element, and that will not occur to the copy `x2`. The same thing applies to `y`:

``````julia> x[begin] = 2;

julia> x, x2
([2], [1])

julia> y[begin] = [3];

julia> y, y2
([[3]], [[2]])
``````

The confusion comes from 1) failing to tell what is being mutated, and 2) failing to distinguish a matrix of scalars from a vector of vectors. A matrix works fine:

``````julia> function test_func2()
A=zeros(Float64, 2, 2) # makes a 2x2 matrix
B=copy(A)
for ja in 1:2, jb in 1:2
(B[ja, jb],A[ja, jb])=(1.0,2.0) # mutates B and A, not its elements
end
print(B)
print(A)
end
test_func2 (generic function with 1 method)

julia> test_func2()
[1.0 1.0; 1.0 1.0][2.0 2.0; 2.0 2.0]
``````

but so would a vector of vectors if you mutated the outer vector instead of the inner ones, though this is much worse than a proper matrix for other reasons:

``````julia> function test_func3()
A=[zeros(Float64,2) for _ in 1:2]
B=copy(A)
for ja in 1:2
(B[ja],A[ja])=([1.0, 1.0],[2.0, 2.0]) # mutates B and A, not its elements
end
print(B)
print(A)
end
test_func3 (generic function with 1 method)

julia> test_func3()
[[1.0, 1.0], [1.0, 1.0]][[2.0, 2.0], [2.0, 2.0]]
``````

I assume it’s written this way for the purpose of a MWE, there are much better ways to make arrays filled with the same number.

2 Likes