Composite types with array type fields

I am trying to understand the strange behavior I see in the code below.

struct A
    m::Vector{Int32}
end

struct B
    m::Vector{Vector{Int32}}
end

let
    # Here, the assigment works like a pointer assigment
    a1 = A([1,2,3])
    a2 = A(a1.m)
    @show a1.m === a2.m
    a1.m[1] = 2
    @show a1, a2
    # Here it performs a copy
    arr = [1,2,3]
    a3 = A(arr)
    @show a3.m === arr
    arr[1] = 2
    @show arr, a3
    # The same thing here
    b1 = B([[1,2],[3]])
    b2 = B(b1.m)
    @show b1.m === b2.m
    b1.m[1] = [1]
    @show b1, b2
    arr = [[1,2],[3]]
    b3 = B(arr)
    @show b3.m === arr
    # and it's a deepcopy
    arr[1][1] = 2
    @show arr, b3
end

The output I get is

a1.m === a2.m = true
(a1, a2) = (A(Int32[2, 2, 3]), A(Int32[2, 2, 3]))
a3.m === arr = false
(arr, a3) = ([2, 2, 3], A(Int32[1, 2, 3]))
b1.m === b2.m = true
(b1, b2) = (B(Vector{Int32}[[1], [3]]), B(Vector{Int32}[[1], [3]]))
b3.m === arr = false
(arr, b3) = ([[2, 2], [3]], B(Vector{Int32}[[1, 2], [3]]))

I find this really strange. I was hoping to use an immutable struct as a simple container for a few pointers, but it seems that in some cases it performs a deepcopy of the input array. What is the difference between these two cases, how do I know when it’s only the pointer being copied vs. the resource handled by the pointer being copied? (I am assuming that the behavior here extends to mutable type fields in general.)

arr = [1,2,3]
a3 = A(arr)

The type of arr is not the same as the m field, so there must be a conversion, and the result of the conversion is clearly not === the original. If you don’t want the conversion, use arr = Int32[1,2,3].

In the first case, you are only comparing the fields, post-conversion.

That was quick, thanks a lot! I had forgotten about type conversion.